home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997…eptember: Technology Seed / ATS Aug-Sept '97.toast / Navigation Services SDK / Examples / SimpleText / SimpleText ƒ / SimpleText.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-18  |  145.8 KB  |  5,554 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        SimpleText.c
  3.  
  4.     Contains:    xxx put contents here xxx
  5.  
  6.     Version:    xxx put the technology version here xxx
  7.  
  8.     Copyright:    © 1997 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     File Ownership:
  11.  
  12.         DRI:                Yan Arrouye
  13.  
  14.         Other Contact:        xxx put alternate contact (owner’s functional manager) here xxx
  15.  
  16.         Technology:            xxx put the technology group name here xxx
  17.  
  18.     Writers:
  19.  
  20.         (Yan)    Yan Arrouye
  21.  
  22.     Change History (most recent first):
  23.  
  24.          <4>    /19/1997    Yan        Mixed mode support
  25.          <3>     4/11/97    Yan        Added support for windows staggering
  26. */
  27.  
  28. /*
  29.     File:        SimpleText.c
  30.  
  31.     Contains:    SimpleText - a simple document editing application for shipping
  32.                              with system software.
  33.  
  34.     Version:    SimpleText 1.4 or later
  35.  
  36. ** Copyright 1993-1996 Apple Computer. All rights reserved.
  37. **
  38. **    You may incorporate this sample code into your applications without
  39. **    restriction, though the sample code has been provided "AS IS" and the
  40. **    responsibility for its operation is 100% yours.  However, what you are
  41. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  42. **    after having made changes. If you're going to re-distribute the source,
  43. **    we require that you make it clear in the source that the code was
  44. **    descended from Apple Sample Code, but that you've made changes.
  45.  
  46. */
  47.  
  48. #include "MacIncludes.h"
  49.  
  50. #include <ImageCompression.h>    // for CustomGetFilePreview
  51. #include <Threads.h>
  52.  
  53. #define CompilingMain=1
  54. #include "SimpleText.h"
  55. #include "Clipboard.h"
  56.  
  57. #include "NavigationServicesSupport.h"
  58.  
  59.  
  60. // --------------------------------------------------------------------------------------------------------------
  61. // INTERNAL TYPES AND TYPEDEFS
  62. // --------------------------------------------------------------------------------------------------------------
  63.  
  64.  
  65. // refCon value between SimpleCatchShape and GXInstallQDTranslator    
  66. typedef struct
  67.     {
  68.     gxShape         thePage;
  69.     gxRectangle        thePageRectangle;
  70.     Boolean            doLayout;
  71.     gxJob            theJob;
  72.     } CatchRefCon;
  73.  
  74. // --------------------------------------------------------------------------------------------------------------
  75. // FORWARD DECLARES
  76. // --------------------------------------------------------------------------------------------------------------
  77. OSErr     DoActivate(WindowRef pWindow, Boolean activating);
  78. OSErr    DoCommand(WindowRef pWindow, short commandID, long menuResult);
  79. OSErr    DoKeyEvent(WindowRef pWindow, EventRecord * pEvent, Boolean processPageControls);
  80. Boolean CommandToIDs(short commandID, short * menuID, short *itemID);
  81. void     AdjustMenus(WindowRef pWindow, Boolean editDialogs, Boolean forceTitlesOn);
  82.  
  83. // --------------------------------------------------------------------------------------------------------------
  84. // GLOBAL VARIABLES
  85. // --------------------------------------------------------------------------------------------------------------
  86. EventRecord            gEvent;                    // currently pending event
  87. Boolean                gAllDone;                // true if the application is the in process of terminating
  88. MachineInfoRec        gMachineInfo;            // info about abilities and options installed on this machine
  89. short                gApplicationResFile;    // resource fork of application
  90. RgnHandle            gCursorRgn;                // region to control the cursor apearence
  91. AGRefNum            gAGRefNum = -1;            // AppleGuide database which is open
  92. FSSpec                gAGSpec;                // where to find our database
  93. AGCoachRefNum        gAGCoachRefNum = -1;    // coach handler refNum
  94. FontMappingHandle    gFontMappingList = nil;    // list of font mappings
  95. ThreadID            gFontThread;            // thread that builds font menu
  96. ThreadID            gAGThread;                // thread that looks for AppleGuide database
  97. ThreadID            gStarterThread;            // starts our other threads for us
  98. Boolean                gDontYield;                // whether our threads should yield
  99. void*                gThreadResults;            // scratch space for thread results
  100.  
  101. // These variables are for the find/replace commands
  102. Str255            gFindString = "\p", gReplaceString = "\p";
  103. Boolean            gWrapAround = false, gCaseSensitive = false;
  104.  
  105. // Metrowerks MWCRuntime.lib defines qd for us on PPC, and their
  106. // __runtime module does under the 68K case. OTOH, neither SC nor
  107. // MrC give us qd for free, so we need it there. I'm still not
  108. // certain which way to go for the ThinkC or Symantec PPC case.
  109. #if !defined(__MWERKS__)
  110. // QuickDraw globals
  111. QDGlobals        qd;
  112. #endif
  113.  
  114. // --------------------------------------------------------------------------------------------------------------
  115. #pragma segment Utility
  116.  
  117. static pascal Boolean AlertFilter(DialogRef theDialog, EventRecord *theEvent, short *itemHit)
  118. {
  119.     if (theEvent->what == activateEvt && (DialogRef) theEvent->message == theDialog)
  120.         {
  121.         SetDialogDefaultItem(theDialog, 1);
  122.         }
  123.  
  124.     if (StdFilterProc(theDialog, theEvent, itemHit))
  125.         return true;
  126.  
  127.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  128.     // drastically changing how the system handles the menu bar during our alert)
  129.     if (theEvent->what == updateEvt /* || theEvent->what == activateEvt */ )
  130.         {
  131.         HandleEvent(theEvent);
  132.         }
  133.  
  134.     return false;
  135.  
  136. } // AlertFilter
  137.  
  138.  
  139. void ConductErrorDialog(OSErr error, short commandID, short alertType)
  140. {
  141.     long        foundError;            // The error, converted to a number
  142.     short        stringIndex;        // Index into the strings
  143.     Str255        errorText;            // the error in a string format
  144.     
  145.     // Start with no error so far
  146.     foundError = 0;
  147.     
  148.     // Start with the first string
  149.     stringIndex = 1;
  150.     
  151.     // Loop until we find an error string
  152.     errorText[0] = 0;
  153.  
  154.     do
  155.         {
  156.         // Get the string, and convert it to a number
  157.         GetIndString(errorText, kErrorBaseID + commandID, stringIndex);
  158.         if (errorText[0] == 0)
  159.             break;
  160.         StringToNum(errorText, &foundError);
  161.         
  162.         // If we reach the last string, or we match the error code
  163.         if ((foundError == 0) ||
  164.             (foundError == error))
  165.             {
  166.             // Get the text string for this error
  167.             GetIndString(errorText, kErrorBaseID + commandID, stringIndex+1);
  168.             }
  169.         else
  170.             {
  171.             // Otherwise, make us continue until we get a string
  172.             errorText[0] = 0;
  173.             }
  174.             
  175.         // Advance so we get the next string number
  176.         stringIndex += 2;
  177.         
  178.         } while (errorText[0] == 0);                // errorText[0] == 0
  179.         
  180.     if (errorText[0] != 0)
  181.         {
  182.         DialogRef    dPtr;
  183.         short        hit;
  184.         
  185.         SetCursor(&qd.arrow);
  186.         ParamText(errorText, "\p", "\p", "\p");
  187.         
  188.         #if !GENERATINGPOWERPC
  189.             if (gMachineInfo.theEnvirons.systemVersion < 0x0700)
  190.                 {
  191.                 short ** hDialog;
  192.                 
  193.                 hDialog = (short**) GetResource('DLOG', kErrorBaseID + alertType);
  194.                 (*hDialog)[4] = dBoxProc;
  195.                 
  196.                 dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  197.                 
  198.                 do
  199.                     {
  200.                     ModalDialog(nil, &hit);
  201.                     } while (hit != ok);
  202.                 
  203.                 DisposeDialog(dPtr);
  204.                 }
  205.             else
  206.                 {
  207.                 dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  208.                 
  209.                 SetDialogDefaultItem(dPtr, ok);
  210.                 
  211.                 BeginMovableModal();
  212.                 
  213.                 do
  214.                     {
  215.                     MovableModalDialog(nil, &hit);
  216.                     } while (hit != ok);
  217.                 
  218.                 DisposeDialog(dPtr);
  219.                 EndMovableModal();
  220.                 }
  221.         #else
  222.             dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  223.             
  224.             SetDialogDefaultItem(dPtr, ok);
  225.             
  226.             BeginMovableModal();
  227.             
  228.             do
  229.                 {
  230.                 MovableModalDialog(nil, &hit);
  231.                 } while (hit != ok);
  232.             
  233.             DisposeDialog(dPtr);
  234.             EndMovableModal();
  235.         #endif
  236.         }
  237.         
  238. } // ConductErrorDialog
  239.  
  240. // --------------------------------------------------------------------------------------------------------------
  241. #pragma segment Utility
  242.  
  243. static void MovableModalMenus(DialogRef dPtr, short *pItem, long menuResult)
  244. {
  245.     short    iCut, iCopy, iClear, iPaste;
  246.     short    editMenu;
  247.     short    menuItem = menuResult & 0xFFFF;
  248.     
  249.     // find out where edit menus are
  250.     CommandToIDs(cCut, &editMenu, &iCut);
  251.     CommandToIDs(cCopy, &editMenu, &iCopy);
  252.     CommandToIDs(cClear, &editMenu, &iClear);
  253.     CommandToIDs(cPaste, &editMenu, &iPaste);
  254.     
  255.     HiliteMenu(0);
  256.     switch (menuResult >> 16)
  257.         {
  258.         case mApple:
  259.             {
  260.             Str255    tempString;
  261.             
  262.             GetMenuItemText(GetMenuHandle(menuResult>>16), menuItem, tempString);
  263.             OpenDeskAcc(tempString);
  264.             }
  265.             break;
  266.             
  267.         case mEdit:
  268.             {
  269.             short    type;
  270.             Handle    item;
  271.             Rect    box;
  272.             short    editField = GetDialogKeyboardFocusItem(dPtr);
  273.             
  274.             // return typed item, if it isn't disabled
  275.             GetDialogItem(dPtr, editField, &type, &item, &box);
  276.             if ((type & itemDisable) == 0)
  277.                 *pItem = editField;
  278.                 
  279.             if (menuItem == iCut)
  280.                 {
  281.                 DialogCut(dPtr);
  282.                 ZeroScrap();
  283.                 TEToScrap();
  284.                 }
  285.                 
  286.             if (menuItem == iCopy)
  287.                 {
  288.                 DialogCopy(dPtr);
  289.                 ZeroScrap();
  290.                 TEToScrap();
  291.                 }
  292.                 
  293.             if (menuItem == iClear)
  294.                 DialogDelete(dPtr);
  295.                 
  296.             if (menuItem == iPaste)
  297.                 DialogPaste(dPtr);
  298.             }
  299.             break;
  300.         }
  301.         
  302. } // MovableModalMenus
  303.  
  304. // --------------------------------------------------------------------------------------------------------------
  305. #pragma segment Utility
  306.  
  307. void MovableModalDialog(ModalFilterProcPtr filterProc, short *pItem)
  308. /*
  309.     Call this as you would ModalDialog, when the dialog is moveable
  310.     modal.
  311.     
  312.     However, first call BeginMovableModal, and afterwards (after
  313.     disposing of dialog) call EndMovableModal.
  314. */
  315. {
  316.     GrafPtr     curPort;
  317.     DialogRef    dPtr = FrontWindow();
  318.     
  319.     *pItem = 0;    
  320.     if (dPtr)
  321.         {
  322.         GetPort(&curPort);
  323.         SetPort(dPtr);
  324.         
  325.         do
  326.             {
  327.             WaitNextEvent(mDownMask + mUpMask + keyDownMask + keyUpMask + autoKeyMask + updateMask + activMask + osMask,
  328.                             &gEvent, 0, nil);
  329.             
  330.             // call the filter proc
  331.             if ( (filterProc) && ((*filterProc) (dPtr, &gEvent, pItem)) )
  332.                 break;
  333.                             
  334.             // call the basic filtering
  335.             if (StdFilterProc(dPtr, &gEvent, pItem))
  336.                 break;
  337.                 
  338.             // handle keyboard
  339.             if ((gEvent.what == keyDown) && (gEvent.modifiers & cmdKey))
  340.                 {
  341.                 MovableModalMenus(dPtr, pItem, MenuKey(gEvent.message & charCodeMask));
  342.                 break;
  343.                 }
  344.                 
  345.             // handle clicks and drags
  346.             if (gEvent.what == mouseDown)
  347.                 {
  348.                 WindowRef    whichWindow;
  349.                 short        part = FindWindow(gEvent.where, &whichWindow);
  350.                 
  351.                 // menu bar events
  352.                 if (part == inMenuBar)
  353.                     {
  354.                     MovableModalMenus(dPtr, pItem, MenuSelect(gEvent.where));
  355.                     break;
  356.                     }
  357.                     
  358.                 // check for outside of our window
  359.                 if (!PtInRgn(gEvent.where, ((WindowPeek)dPtr)->strucRgn))
  360.                     {
  361.                     SysBeep(1);
  362.                     gEvent.what = nullEvent;
  363.                     }
  364.                     
  365.                 // drag the window around
  366.                 if ( (part == inDrag) && (whichWindow == dPtr) )
  367.                     {
  368.                     Rect    tempRect = (**GetGrayRgn()).rgnBBox;
  369.                     
  370.                     DragWindow(GetDialogWindow(dPtr), gEvent.where, &tempRect);
  371.                     gEvent.what = nullEvent;
  372.                     }
  373.                 }
  374.                 
  375.             // check with standard dialog stuff    
  376.             {
  377.             DialogRef    tempDialog;
  378.             
  379.             if ( IsDialogEvent(&gEvent) && DialogSelect(&gEvent, &tempDialog, pItem) )
  380.                 break;
  381.             }
  382.             
  383.             // handle updates
  384.             if (gEvent.what == updateEvt)
  385.                 {
  386.                 HandleEvent(&gEvent);
  387.                 break;
  388.                 }
  389.             } while (true);
  390.         
  391.         SetPort(curPort);
  392.         }
  393.         
  394. } // MovableModalDialog
  395.  
  396. // --------------------------------------------------------------------------------------------------------------
  397. #pragma segment Utility
  398.  
  399. void BeginMovableModal(void)
  400. {
  401.     DialogRef    dPtr = FrontWindow();
  402.     WindowRef    nextWindow = GetNextWindow(dPtr);
  403.     
  404.     if (nextWindow)
  405.         DoActivate(nextWindow, false);
  406.     AdjustMenus(GetDialogWindow(dPtr), (GetDialogKeyboardFocusItem(dPtr) > 0), false);
  407.  
  408. } // BeginMovableModal
  409.  
  410. // --------------------------------------------------------------------------------------------------------------
  411. #pragma segment Utility
  412.  
  413. void EndMovableModal(void)
  414. {
  415.     WindowRef    nextWindow = FrontWindow();
  416.     
  417.     AdjustMenus(nextWindow, true, false);
  418.     if (nextWindow)
  419.         DoActivate(nextWindow, true);
  420.     
  421. } // EndMovableModal
  422.  
  423. // --------------------------------------------------------------------------------------------------------------
  424. #pragma segment Utility
  425.  
  426. short ConductFindOrReplaceDialog(short dialogID)
  427. {
  428.     DialogRef    dPtr;
  429.     short        hit;
  430.     
  431.     dPtr = GetNewDialog(dialogID, nil, (WindowRef)-1);
  432.     if (dPtr)
  433.         {
  434.         short    kind;
  435.         Rect    box;
  436.         Handle    item;
  437.         
  438.         // standard default behavior
  439.         SetDialogDefaultItem(dPtr, ok);
  440.         SetDialogCancelItem (dPtr, cancel);
  441.         SetDialogTracksCursor(dPtr, true);
  442.         
  443.         // Find string
  444.         GetDialogItem(dPtr, iFindEdit, &kind, &item, &box);
  445.         SetDialogItemText(item, gFindString);
  446.  
  447.         // check boxes
  448.         GetDialogItem(dPtr, iCaseSensitive, &kind, &item, &box);
  449.         SetControlValue((ControlRef)item, gCaseSensitive);
  450.         GetDialogItem(dPtr, iWrapAround, &kind, &item, &box);
  451.         SetControlValue((ControlRef)item, gWrapAround);
  452.         
  453.         if (dialogID == kReplaceWindowID)
  454.             {
  455.             // Replace string
  456.             GetDialogItem(dPtr, iReplaceEdit, &kind, &item, &box);
  457.             SetDialogItemText(item, gReplaceString);
  458.             }
  459.         
  460.         // select the search text by default
  461.         SelectDialogItemText(dPtr, iFindEdit, 0, 32767);
  462.         
  463.         // and away we go!
  464.         ShowWindow(GetDialogWindow(dPtr));
  465.         BeginMovableModal();
  466.         
  467.         do
  468.             {
  469.             MovableModalDialog(nil, &hit);
  470.             switch (hit)
  471.                 {
  472.                 case iCaseSensitive:
  473.                 case iWrapAround:
  474.                     GetDialogItem(dPtr, hit, &kind, &item, &box);
  475.                     SetControlValue((ControlRef)item, 1-GetControlValue((ControlRef)item));
  476.                     break;
  477.                 }
  478.             } while ( (hit != ok) && (hit != cancel) && (hit != iReplaceAll) );
  479.         
  480.         if (hit != cancel)
  481.             {
  482.             // Find string
  483.             GetDialogItem(dPtr, iFindEdit, &kind, &item, &box);
  484.             GetDialogItemText(item, gFindString);
  485.     
  486.             // check boxes
  487.             GetDialogItem(dPtr, iCaseSensitive, &kind, &item, &box);
  488.             gCaseSensitive = GetControlValue((ControlRef)item);
  489.             GetDialogItem(dPtr, iWrapAround, &kind, &item, &box);
  490.             gWrapAround = GetControlValue((ControlRef)item);
  491.             
  492.             if (dialogID == kReplaceWindowID)
  493.                 {
  494.                 // Replace string
  495.                 GetDialogItem(dPtr, iReplaceEdit, &kind, &item, &box);
  496.                 GetDialogItemText(item, gReplaceString);
  497.                 }
  498.             }
  499.             
  500.         DisposeDialog(dPtr);
  501.         EndMovableModal();
  502.         }
  503.         
  504.     return(hit);
  505.     
  506. } // ConductFindOrReplaceDialog
  507.  
  508. // --------------------------------------------------------------------------------------------------------------
  509. #pragma segment Utility
  510.  
  511. void SetWatchCursor(void)
  512. {
  513.     CursHandle    theWatch;
  514.         
  515.     theWatch = GetCursor(watchCursor);
  516.     if (theWatch)
  517.         {
  518.         char    oldState;
  519.         
  520.         oldState = HGetState((Handle) theWatch);
  521.         HLock((Handle) theWatch);
  522.         SetCursor(*theWatch);
  523.         HSetState((Handle) theWatch, oldState);
  524.         }
  525.         
  526. } // SetWatchCursor
  527.  
  528. // --------------------------------------------------------------------------------------------------------------
  529. #pragma segment Utility
  530.  
  531. void LongRectToRect(LongRect* longRect, Rect *rect)
  532. {
  533.     rect->top         = longRect->top;
  534.     rect->left         = longRect->left;
  535.     rect->bottom     = longRect->bottom;
  536.     rect->right     = longRect->right;
  537.     
  538. } // LongRectToRect
  539.  
  540. // --------------------------------------------------------------------------------------------------------------
  541. #pragma segment Utility
  542.  
  543. void RectToLongRect(Rect *rect, LongRect *longRect)
  544. {
  545.     longRect->top         = rect->top;
  546.     longRect->left         = rect->left;
  547.     longRect->bottom     = rect->bottom;
  548.     longRect->right     = rect->right;
  549.     
  550. } // RectToLongRect
  551.  
  552. // --------------------------------------------------------------------------------------------------------------
  553. #pragma segment Utility
  554.  
  555. void GetPICTRectangleAt72dpi(PicHandle hPicture, Rect *pictureRect)
  556. {
  557.     typedef struct FixedRect {
  558.         Fixed left;
  559.         Fixed top;
  560.         Fixed right;
  561.         Fixed bottom;
  562.     } FixedRect;
  563.     
  564.     typedef struct {
  565.         Picture                pictInfo;
  566.         unsigned short        versionOp;        // 0x1101
  567.         Byte                opCodes[1];
  568.     } PICTHeaderVer1;
  569.     
  570.     typedef struct {
  571.         Picture            pictInfo;
  572.         unsigned short    versionOp;        // 0x0011
  573.         unsigned short    versionOp2;        // 0x02ff
  574.         unsigned short    headerOp;        // 0x0c00
  575.         unsigned short    version;        // 0xffff
  576.         unsigned short    version2;        // 0xffff
  577.         FixedRect        pictBounds;
  578.         unsigned long    reserved;
  579.         unsigned short    opCodes[1];
  580.     } PICTHeaderVer2;
  581.     
  582.     typedef struct {
  583.         Picture            pictInfo;
  584.         unsigned short    versionOp;        // 0x0011
  585.         unsigned short    versionOp2;        // 0x02ff
  586.         unsigned short    headerOp;        // 0x0c00
  587.         unsigned short    version;        // 0xfffe
  588.         unsigned short    reserved;        // 0x0000
  589.         Fixed            hRes;
  590.         Fixed            vRes;
  591.         Rect            pictBounds;
  592.         unsigned long    reserved2;
  593.         unsigned short    opCodes[1];
  594.     } PICTHeaderVer2Ext;
  595.  
  596.     Fixed            hRes, vRes;
  597.     PICTHeaderVer1* pPict = (PICTHeaderVer1*) *hPicture;
  598.  
  599.     hRes = vRes = ff(72);        // assume 72 dpi
  600.  
  601.     if (pPict->versionOp == 0x0011) 
  602.         {    
  603.         // Version 2 PICT
  604.     
  605.         PICTHeaderVer2* pPict2 = (PICTHeaderVer2*) pPict;
  606.         
  607.         if (pPict2->version == 0xfffe) 
  608.             {    
  609.             // Extended Version 2
  610.             PICTHeaderVer2Ext* pPict2ext = (PICTHeaderVer2Ext*) pPict;
  611.             hRes = pPict2ext->hRes;
  612.             vRes = pPict2ext->vRes;
  613.             }
  614.         }
  615.  
  616.     hRes = FixDiv(hRes, ff(72));
  617.     vRes = FixDiv(vRes, ff(72));
  618.     pictureRect->left     = Fix2Long(FixDiv( ff((**hPicture).picFrame.left),         hRes ));
  619.     pictureRect->right     = Fix2Long(FixDiv( ff((**hPicture).picFrame.right),     hRes ));
  620.     pictureRect->top     = Fix2Long(FixDiv( ff((**hPicture).picFrame.top),         vRes ));
  621.     pictureRect->bottom = Fix2Long(FixDiv( ff((**hPicture).picFrame.bottom),     vRes ));
  622.     
  623. } // GetPICTRectangleAt72dpi
  624.  
  625. // --------------------------------------------------------------------------------------------------------------
  626. #pragma segment Utility
  627.  
  628. static WindowDataPtr    GetWindowInfo(WindowRef pWindow)
  629. {
  630.     WindowDataPtr result = nil;
  631.     
  632.     if     (
  633.         (pWindow) &&
  634.         (GetWindowKind(pWindow) == userKind)
  635.         )
  636.         result = (WindowDataPtr) GetWRefCon(pWindow);
  637.  
  638.     return result;
  639.     
  640. } // GetWindowInfo
  641.  
  642. // --------------------------------------------------------------------------------------------------------------
  643. #pragma segment Utility
  644.  
  645. static short ZeroStringSub(Str255 destString, Str255 subStr)
  646.     // returns number of substitutions performed
  647. {
  648.     OSErr    anErr;
  649.     Handle    destHandle = nil;
  650.     Handle    subHandle = nil;
  651.     short    count = 0;
  652.  
  653.     anErr = PtrToHand(&destString[1], &destHandle, destString[0]);
  654.     if (anErr == noErr)
  655.         {        
  656.         anErr = PtrToHand(&subStr[1], &subHandle, subStr[0]);
  657.         if (anErr == noErr)
  658.             {
  659.             count = ReplaceText(destHandle, subHandle, "\p^0");        // error or # of substitutions
  660.                         
  661.             destString[0] = GetHandleSize(destHandle);
  662.             BlockMoveData(*destHandle, &destString[1], destString[0]);
  663.             }
  664.         }
  665.  
  666.     DisposeHandle(destHandle);
  667.     DisposeHandle(subHandle);
  668.  
  669.     if (count < 0)
  670.         count = 0;        // change error code into count = 0 substitutions
  671.  
  672.     return count;
  673.  
  674. } // ZeroStringSub
  675.  
  676. // --------------------------------------------------------------------------------------------------------------
  677. // SEARCH/REPLACE UTILITY FUNCTIONS
  678. // --------------------------------------------------------------------------------------------------------------
  679. static Boolean IsThisTheString(
  680.             Ptr p,                        // pointer to check
  681.             Str255 searchString,        // string to check for
  682.             Boolean isCaseSensitive)    // case sensitive check or not
  683. /*
  684.     Returns true if the supplied string is at the specified offset.
  685.     Otherwise returns false.
  686. */
  687. {
  688.     Boolean    returnValue = false;
  689.     
  690.     if (isCaseSensitive)
  691.         returnValue = ( IUMagString(p, &searchString[1], searchString[0], searchString[0]) == 0 );
  692.     else
  693.         returnValue = ( IUMagIDString(p, &searchString[1], searchString[0], searchString[0]) == 0 );
  694.         
  695.     return(returnValue);
  696.     
  697. } // IsThisTheString
  698.  
  699. // --------------------------------------------------------------------------------------------------------------
  700.  
  701. Boolean PerformSearch(
  702.         Handle    h,                    // handle to search
  703.         long start,                    // offset to begin with
  704.         Str255 searchString,        // string to search for
  705.         Boolean isCaseSensitive,    // case sensitive search
  706.         Boolean isBackwards,        // search backwards from starting point
  707.         Boolean isWraparound,        // wrap search around from end->begining
  708.         long * pNewStart,            // returned new selection start
  709.         long * pNewEnd)                // returned new selection end
  710. /*
  711.     Performs a search on the supplied handle, starting at the provided
  712.     offset.  Returns the new selection start and end values, and true
  713.     if the search is successful.  Otherwise it returns false.
  714. */
  715. {
  716.     char    flags;
  717.     Ptr        startPtr;
  718.     Ptr        endPtr;
  719.     Ptr        searchPtr;
  720.     Boolean    foundIt = false;
  721.     
  722.     flags = HGetState(h);
  723.     HLock(h);
  724.             
  725.     // back up one when searching backwards, or we'll hit every time on the current
  726.     // character
  727.     if (isBackwards)
  728.         {
  729.         if (start != 0)
  730.             {
  731.             --start;
  732.             }
  733.         else
  734.             {
  735.             if (isWraparound)
  736.                 start = GetHandleSize(h);
  737.             else
  738.                 return(false);
  739.             }
  740.         }
  741.         
  742.     // determine the bounds of the searching
  743.     startPtr = (*h) + start;
  744.     if ( isWraparound )
  745.         {
  746.         if (isBackwards)
  747.             {
  748.             // go backwards until just after the start, or begining of
  749.             // document is start is the end
  750.             if (start == GetHandleSize(h))
  751.                 endPtr = *h;
  752.             else
  753.                 endPtr = startPtr + 1;
  754.             }
  755.         else
  756.             {
  757.             // go forwards until just before the start, or to the end
  758.             // of the document is the start is already the begining
  759.             if (start == 0)
  760.                 endPtr = *h + GetHandleSize(h);
  761.             else
  762.                 endPtr = startPtr - 1;
  763.             }
  764.         }
  765.     else
  766.         {
  767.         if (isBackwards)
  768.             {
  769.             // go back until hit begining of document
  770.             endPtr = *h-1;    
  771.             }
  772.         else
  773.             {
  774.             // go forward until hit end of document
  775.             endPtr = *h + GetHandleSize(h);
  776.             }
  777.         }
  778.         
  779.     searchPtr = startPtr;
  780.     while (searchPtr != endPtr)
  781.         {
  782.         if (IsThisTheString(searchPtr, searchString, isCaseSensitive))
  783.             {
  784.             foundIt = true;
  785.             *pNewStart = searchPtr - *h;
  786.             *pNewEnd = *pNewStart + searchString[0];
  787.             break;
  788.             }
  789.             
  790.         if (isBackwards)
  791.             --searchPtr;
  792.         else
  793.             ++searchPtr;
  794.             
  795.         if (isWraparound)
  796.             {
  797.             if (searchPtr < *h)
  798.                 searchPtr = *h + GetHandleSize(h);
  799.             if (searchPtr > *h + GetHandleSize(h))
  800.                 searchPtr = *h;
  801.             }
  802.         }
  803.         
  804.     HSetState(h, flags);
  805.     
  806.     return(foundIt);
  807.     
  808. } // PerformSearch
  809.  
  810. // --------------------------------------------------------------------------------------------------------------
  811. // SELECTION UTILITY ROUTINES
  812. // --------------------------------------------------------------------------------------------------------------
  813. void DrawSelection(WindowDataPtr pData, Rect *pSelection, short * pPhase, Boolean bumpPhase)
  814. {
  815.     if    (!EmptyRect(pSelection) ) 
  816.         {
  817.         RgnHandle    oldClip = NewRgn();
  818.         Pattern        aPattern;
  819.         Rect        newClip;
  820.  
  821.         
  822.         if     ( 
  823.             (bumpPhase) && 
  824.             (MOVESELECTION(TickCount()) ) 
  825.             )
  826.             {
  827.             if ((++(*pPhase)) > 7 )
  828.                 *pPhase = 1;
  829.             }
  830.             
  831.         // setup for drawing in this window
  832.         SetPort((GrafPtr) pData);
  833.         GetClip(oldClip);
  834.         PenMode(notPatXor);
  835.         
  836.         // offset the draw area (SetOrigin a must to preserve pattern appearence)
  837.         // and the clip area to avoid stepping on the scroll bars
  838.         SetOrigin(GetControlValue(pData->hScroll), GetControlValue(pData->vScroll));
  839.         newClip = pData->contentRect;
  840.         OffsetRect(&newClip, GetControlValue(pData->hScroll), GetControlValue(pData->vScroll));
  841.         ClipRect(&newClip);
  842.         
  843.         // do the draw
  844.         GetIndPattern(&aPattern, kPatternListID, (*pPhase)+1);
  845.         PenPat(&aPattern);
  846.         FrameRect(pSelection);
  847.         SetOrigin(0, 0);
  848.         
  849.         // restore the old port settings
  850.         SetClip(oldClip);
  851.         DisposeRgn(oldClip);
  852.         PenNormal();
  853.  
  854.         }
  855.  
  856. } // DrawSelection
  857.  
  858. // --------------------------------------------------------------------------------------------------------------
  859. OSErr SelectContents(WindowRef pWindow, WindowDataPtr pData, EventRecord *pEvent, Rect *pSelection, Rect *pContent, short *pPhase)
  860. {
  861.  
  862.     OSErr            anErr = noErr;
  863.     Point            clickPoint = pEvent->where;
  864.     Point            currentPoint;
  865.     Boolean         didJustScroll;
  866.     ControlRef        theControl;
  867.     
  868.     GlobalToLocal(&clickPoint);
  869.     if (FindControl(clickPoint, pWindow, &theControl) == 0)
  870.         {
  871.     
  872.         // move the click point into the proper range
  873.         clickPoint.h += GetControlValue(pData->hScroll);
  874.         clickPoint.v += GetControlValue(pData->vScroll);
  875.         
  876.         // if the shift key is held down then the selection starts from
  877.         // a preexisting point such that we are doing an expand/contract
  878.         // of the original selection
  879.         if (pEvent->modifiers & shiftKey)
  880.             {
  881.             if (clickPoint.h < pSelection->right)
  882.                 clickPoint.h = pSelection->right;
  883.             else
  884.                 clickPoint.h = pSelection->left;
  885.  
  886.             if (clickPoint.v < pSelection->bottom)
  887.                 clickPoint.v = pSelection->bottom;
  888.             else
  889.                 clickPoint.v = pSelection->top;
  890.             }
  891.                         
  892.         while (StillDown())
  893.             {                    
  894.             // get the current mouse 
  895.             GetMouse(¤tPoint);
  896.             
  897.             didJustScroll = false;
  898.             // scroll contents if needed
  899.             {
  900.             short    deltaH = 0;
  901.             short    deltaV = 0;
  902.             
  903.             if (currentPoint.h < 0)
  904.                 deltaH = pData->hScrollAmount;
  905.             if (currentPoint.h > qd.thePort->portRect.right)
  906.                 deltaH = -pData->hScrollAmount;
  907.             if (currentPoint.v < 0)
  908.                 deltaV = pData->vScrollAmount;
  909.             if (currentPoint.v > qd.thePort->portRect.bottom)
  910.                 deltaV = -pData->vScrollAmount;
  911.                 
  912.             if ( (deltaH != 0) || (deltaV != 0) )
  913.                 {                
  914.                 if (deltaH)
  915.                     SetControlAndClipAmount(pData->hScroll, &deltaH);
  916.                 if (deltaV)
  917.                     SetControlAndClipAmount(pData->vScroll, &deltaV);
  918.  
  919.                 DoScrollContent(pWindow, pData, deltaH, deltaV);
  920.                 
  921.                 didJustScroll = true;
  922.                 }
  923.             }
  924.             
  925.             // map mouse into proper range
  926.             currentPoint.h += GetControlValue(pData->hScroll);
  927.             currentPoint.v += GetControlValue(pData->vScroll);
  928.     
  929.             // clip to the document size
  930.             if (currentPoint.h < 0)
  931.                 currentPoint.h = 0;
  932.             if (currentPoint.v < 0)
  933.                 currentPoint.v = 0;
  934.             if (currentPoint.h > pContent->right)
  935.                 currentPoint.h = pContent->right;
  936.             if (currentPoint.v > pContent->bottom)
  937.                 currentPoint.v = pContent->bottom;
  938.                 
  939.             // draw the new selection if it is time or we are about to 
  940.             // exit this loop
  941.             if ((MOVESELECTION(TickCount())) || (!Button()) || (didJustScroll) )
  942.                 {
  943.                 // first, erase any old selection we might have had
  944.                 DrawSelection(pData, pSelection, pPhase, false);
  945.  
  946.                 // make a rectangle out of the two points
  947.                 pSelection->left     = Min(currentPoint.h, clickPoint.h);
  948.                 pSelection->right     = Max(currentPoint.h, clickPoint.h);
  949.                 pSelection->top     = Min(currentPoint.v, clickPoint.v);
  950.                 pSelection->bottom     = Max(currentPoint.v, clickPoint.v);
  951.     
  952.                 // draw the new selection
  953.                 DrawSelection(pData, pSelection, pPhase, true);
  954.                 }
  955.             }
  956.         
  957.         // we handled the selection
  958.         anErr = eActionAlreadyHandled;
  959.         }
  960.         
  961.     return(anErr);
  962.     
  963. } // SelectContents
  964.  
  965. // --------------------------------------------------------------------------------------------------------------
  966. void DragAndDropArea(WindowRef pWindow, WindowDataPtr pData, EventRecord* event, Rect *pFrameRect)
  967. {
  968.     RgnHandle        hilightRgn;
  969.     Rect            r;
  970.     DragReference    theDrag;
  971.     OSErr            anErr = noErr;
  972.     
  973.     if (NewDrag(&theDrag) == noErr)
  974.         {
  975.         if (pData->pDragAddFlavors)
  976.             anErr = (*(pData->pDragAddFlavors)) (pWindow, pData, theDrag);
  977.         
  978.         if (anErr == noErr)
  979.             {
  980.             Rect    globalRect = *pFrameRect;
  981.             
  982.             hilightRgn = NewRgn();    
  983.             LocalToGlobal(&TopLeft(globalRect));
  984.             LocalToGlobal(&BotRight(globalRect));
  985.             RectRgn(hilightRgn, &globalRect);
  986.             SetDragItemBounds(theDrag, 1, &r);
  987.     
  988.             // turn the region from a fill into a frame
  989.             {    
  990.                 RgnHandle tempRgn = NewRgn();
  991.     
  992.                 CopyRgn(hilightRgn, tempRgn);
  993.                 InsetRgn(tempRgn, 1, 1);
  994.                 DiffRgn(hilightRgn, tempRgn, hilightRgn);
  995.                 DisposeRgn(tempRgn);
  996.             }
  997.             
  998.             TrackDrag(theDrag, event, hilightRgn);
  999.             DisposeDrag(theDrag);
  1000.             DisposeRgn(hilightRgn);
  1001.             }
  1002.         }
  1003.  
  1004. } // DragAndDropArea
  1005.  
  1006. // --------------------------------------------------------------------------------------------------------------
  1007. // WINDOW UTILITY ROUTINES
  1008. // --------------------------------------------------------------------------------------------------------------
  1009. #pragma segment Main
  1010.  
  1011. static void CalculateGrowIcon(WindowDataPtr pData, Rect * location)
  1012. {
  1013.     if (pData->vScroll)
  1014.         location->top = (**pData->vScroll).contrlRect.bottom;
  1015.     else
  1016.         {
  1017.         if (pData->hScroll)
  1018.             location->top = (**pData->hScroll).contrlRect.top;
  1019.         else
  1020.             location->top = pData->theWindow.port.portRect.bottom - 15;
  1021.         }
  1022.         
  1023.     if (pData->hScroll)
  1024.         location->left = (**pData->hScroll).contrlRect.right;
  1025.     else
  1026.         {
  1027.         if (pData->vScroll)
  1028.             location->left = (**pData->vScroll).contrlRect.left;
  1029.         else
  1030.             location->left = pData->theWindow.port.portRect.right - 15;
  1031.         }
  1032.         
  1033.     location->right = location->left + 16;
  1034.     location->bottom = location->top + 16;
  1035.     
  1036. } // CalculateGrowIcon
  1037.  
  1038. // --------------------------------------------------------------------------------------------------------------
  1039. #pragma segment Main
  1040.  
  1041. OSErr    AdjustScrollBars(WindowRef pWindow,
  1042.     Boolean moveControls,                 // might the controls have moved?
  1043.     Boolean didResize,                     // did we just resize the window?
  1044.     Boolean *needInvalidate)            // does the caller need to invalidate contents as a result?
  1045. {
  1046.     OSErr            anErr = noErr;
  1047.     LongRect        docRect;
  1048.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  1049.     Rect            growIconRect;
  1050.     
  1051.     if (needInvalidate)
  1052.         *needInvalidate = false;
  1053.  
  1054.     if (pData)
  1055.         {
  1056.         short    oldHMax, oldVMax;
  1057.         short    oldHValue, oldVValue;
  1058.         
  1059.         // cache current values, we'll force an update if we needed to change em!
  1060.         if (pData->hScroll)
  1061.             {
  1062.             oldHMax = GetControlMaximum(pData->hScroll);
  1063.             oldHValue = GetControlValue(pData->hScroll);
  1064.             }
  1065.         if (pData->vScroll)
  1066.             {
  1067.             oldVMax = GetControlMaximum(pData->vScroll);
  1068.             oldVValue = GetControlValue(pData->vScroll);
  1069.             }
  1070.             
  1071.         // if we have a grow box but not all controls we have to invalidate the grow bar areas
  1072.         // by caclulating them
  1073.         if ( (didResize) && (pData->hasGrow) )
  1074.             {
  1075.             // if we regrow without any scroll bars, we need to update the content area
  1076.             if ( (needInvalidate) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  1077.                 *needInvalidate = true;
  1078.             
  1079.             // invalidate old grow bar areas
  1080.             if (pData->vScroll == nil)
  1081.                 {
  1082.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1083.                 growIconRect.left = pData->contentRect.right;
  1084.                 InvalRect(&growIconRect);
  1085.                 }
  1086.             if (pData->hScroll == nil)
  1087.                 {
  1088.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1089.                 growIconRect.top = pData->contentRect.bottom;
  1090.                 InvalRect(&growIconRect);
  1091.                 }
  1092.             
  1093.             // invalidate new grow bar areas
  1094.             if (pData->vScroll == nil)
  1095.                 {
  1096.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1097.                 growIconRect.left = growIconRect.right - kScrollBarSize;
  1098.                 InvalRect(&growIconRect);
  1099.                 }
  1100.             if (pData->hScroll == nil)
  1101.                 {
  1102.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1103.                 growIconRect.top = growIconRect.bottom - kScrollBarSize;
  1104.                 InvalRect(&growIconRect);
  1105.                 }
  1106.             }
  1107.             
  1108.         // if the controls need moving, recalculate the visible area
  1109.         if (moveControls)
  1110.             {
  1111.             pData->contentRect = GetWindowPort(pWindow)->portRect;
  1112.             if ((pData->hScroll) || (pData->hasGrow) )
  1113.                 pData->contentRect.bottom -= kScrollBarSize;
  1114.             if ((pData->vScroll) || (pData->hasGrow) )
  1115.                 pData->contentRect.right -= kScrollBarSize;
  1116.             }
  1117.             
  1118.         // before doing anything, make the controls invisible
  1119.         if (pData->hScroll)
  1120.             (**pData->hScroll).contrlVis = 0;    
  1121.         if (pData->vScroll)
  1122.             (**pData->vScroll).contrlVis = 0;
  1123.  
  1124.         // based on document and visiable area, adjust possible control values
  1125.         if ( (pData->pGetDocumentRect) && ((pData->hScroll) || (pData->vScroll)) )
  1126.             {
  1127.             // let the object calc the size and content if it wishes to
  1128.             anErr = (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, false);
  1129.             if (anErr == noErr)
  1130.                 {
  1131.                 short    amountOver;
  1132.                 short    newMax;
  1133.                 
  1134.                 amountOver = (docRect.right - docRect.left) - (pData->contentRect.right - pData->contentRect.left);
  1135.                 if     (
  1136.                     (pData->hScroll) &&
  1137.                     (amountOver > 0)
  1138.                     )
  1139.                     newMax = amountOver;
  1140.                 else
  1141.                     newMax = 0;
  1142.     
  1143.                 if (pData->hScroll)
  1144.                     {
  1145.                     if (GetControlValue(pData->hScroll) > newMax)
  1146.                         {
  1147.                         if (needInvalidate)
  1148.                             *needInvalidate = true;
  1149.                         }
  1150.                     SetControlMaximum(pData->hScroll, newMax);
  1151.                     }
  1152.                 
  1153.                 amountOver = (docRect.bottom - docRect.top) - (pData->contentRect.bottom - pData->contentRect.top);
  1154.                 if     (
  1155.                     (pData->vScroll) &&
  1156.                     (amountOver > 0)
  1157.                     )
  1158.                     newMax = amountOver;
  1159.                 else
  1160.                     newMax = 0;
  1161.                     
  1162.                 if (pData->vScroll)
  1163.                     {
  1164.                     if (GetControlValue(pData->vScroll) > newMax)
  1165.                         {
  1166.                         if (needInvalidate)
  1167.                             *needInvalidate = true;
  1168.                         }
  1169.                     SetControlMaximum(pData->vScroll, newMax);
  1170.                     }
  1171.                 }
  1172.             }
  1173.             
  1174.         // then, if the controls need moving, we move them and inval the old
  1175.         // and new locations
  1176.         if (moveControls)
  1177.             {
  1178.             // if we have grow box we invalidate the old grow location
  1179.             if ( pData->hasGrow) 
  1180.                 {
  1181.                 CalculateGrowIcon(pData, &growIconRect);
  1182.                 InvalRect(&growIconRect);
  1183.                 }
  1184.                 
  1185.             if (pData->hScroll)
  1186.                 {
  1187.                 short    widthAdjust;
  1188.                 
  1189.                 if ((pData->vScroll) || (pData->hasGrow))
  1190.                     widthAdjust = -kGrowScrollAdjust;
  1191.                 else
  1192.                     widthAdjust = -1;
  1193.                     
  1194.                 growIconRect = (**pData->hScroll).contrlRect;
  1195.                 InvalRect(&growIconRect);
  1196.                 
  1197.                 MoveControl(pData->hScroll, pData->hScrollOffset-1, GetWindowPort(pWindow)->portRect.bottom - kScrollBarSize);
  1198.                 SizeControl(pData->hScroll, (GetWindowPort(pWindow)->portRect.right - 
  1199.                             GetWindowPort(pWindow)->portRect.left) + widthAdjust - pData->hScrollOffset,
  1200.                             16);
  1201.  
  1202.                 growIconRect = (**pData->hScroll).contrlRect;
  1203.                 InvalRect(&growIconRect);
  1204.                 }
  1205.  
  1206.             if (pData->vScroll)
  1207.                 {
  1208.                 short    heightAdjust;
  1209.                 
  1210.                 if ((pData->hScroll) || (pData->hasGrow))
  1211.                     heightAdjust = -kGrowScrollAdjust;
  1212.                 else
  1213.                     heightAdjust = -1;
  1214.                     
  1215.                 growIconRect = (**pData->vScroll).contrlRect;
  1216.                 InvalRect(&growIconRect);
  1217.  
  1218.                 MoveControl(pData->vScroll, GetWindowPort(pWindow)->portRect.right - kScrollBarSize, pData->vScrollOffset-1);
  1219.                 SizeControl(pData->vScroll, 16,
  1220.                             (GetWindowPort(pWindow)->portRect.bottom - 
  1221.                             GetWindowPort(pWindow)->portRect.top) + heightAdjust - pData->vScrollOffset);
  1222.                 growIconRect = (**pData->vScroll).contrlRect;
  1223.                 InvalRect(&growIconRect);
  1224.                 }
  1225.                 
  1226.             // if we have scroll bars, update the grow icon
  1227.             if ( pData->hasGrow )
  1228.                 {
  1229.                 CalculateGrowIcon(pData, &growIconRect);
  1230.                 InvalRect(&growIconRect);
  1231.                 }
  1232.             
  1233.             }
  1234.  
  1235.         // let the document adjust anything it needs to
  1236.         if (pData->pAdjustSize)
  1237.             anErr = (*(pData->pAdjustSize)) (pWindow, pData, &didResize);
  1238.             
  1239.         if ((didResize) && (needInvalidate))
  1240.             *needInvalidate = true;
  1241.  
  1242.  
  1243.         if ( ((WindowPeek) pWindow)->hilited )
  1244.             {
  1245.             // after doing something, make the controls visible
  1246.             if (pData->hScroll)
  1247.                 {
  1248.                 if ((oldHMax != GetControlMaximum(pData->hScroll)) || (oldHValue != GetControlValue(pData->hScroll)) )
  1249.                     ShowControl(pData->hScroll);
  1250.                 else
  1251.                     (**pData->hScroll).contrlVis = 0xFF;    
  1252.                 }
  1253.             if (pData->vScroll)
  1254.                 {
  1255.                 if ((oldVMax != GetControlMaximum(pData->vScroll)) || (oldVValue != GetControlValue(pData->vScroll)) )
  1256.                     ShowControl(pData->vScroll);
  1257.                 else
  1258.                     (**pData->vScroll).contrlVis = 0xFF;
  1259.                 }
  1260.             }
  1261.  
  1262.         }
  1263.         
  1264.     return anErr;
  1265.     
  1266. } // AdjustScrollBars
  1267.  
  1268. // --------------------------------------------------------------------------------------------------------------
  1269. // MENU UTILITY ROUTINES
  1270. // --------------------------------------------------------------------------------------------------------------
  1271. #pragma segment Main
  1272.  
  1273. Boolean CommandToIDs(short commandID, short * menuID, short *itemID)
  1274. {
  1275.  
  1276.     short    ** commandHandle;
  1277.     short    whichMenu;
  1278.     short    oldResFile = CurResFile();
  1279.     Boolean    returnValue = false;
  1280.     
  1281.     UseResFile(gApplicationResFile);
  1282.     for (whichMenu = mApple; whichMenu <= mLastMenu; whichMenu++)
  1283.         {
  1284.         commandHandle = (short**) Get1Resource('MCMD', whichMenu);
  1285.         if (commandHandle)
  1286.             {
  1287.             short    * pCommands = *commandHandle;
  1288.             short    commandIndex;
  1289.             short    numCommands = pCommands[0];
  1290.             
  1291.             for (commandIndex = 1; commandIndex <= numCommands; ++commandIndex)
  1292.                 if (pCommands[commandIndex] == commandID)
  1293.                     {
  1294.                     *menuID = whichMenu;
  1295.                     *itemID = commandIndex;
  1296.                     
  1297.                     returnValue = (commandIndex == numCommands);
  1298.                     }
  1299.             }    
  1300.         }
  1301.         
  1302.     UseResFile(oldResFile);
  1303.     
  1304.     return returnValue;
  1305.     
  1306. } // CommandToIDs
  1307.  
  1308. // --------------------------------------------------------------------------------------------------------------
  1309. #pragma segment Main
  1310.  
  1311. Boolean IsCommandEnabled(short commandID)
  1312. /*
  1313.     returns true if a given command is currently enabled
  1314. */
  1315. {
  1316.     short        whichMenu, whichItem;
  1317.     MenuHandle    menu;
  1318.     
  1319.     CommandToIDs(commandID, &whichMenu, &whichItem);
  1320.     menu = GetMenuHandle(whichMenu);
  1321.     
  1322.     if ((**menu).enableFlags & (1 << whichItem))
  1323.         return(true);
  1324.     
  1325.     return(false);
  1326.     
  1327. } // IsCommandEnabled
  1328.  
  1329. // --------------------------------------------------------------------------------------------------------------
  1330. #pragma segment Main
  1331.  
  1332. void EnableCommand(short commandID)
  1333. /*
  1334.     Given a command ID, enables the first menu item with that command ID.
  1335.     
  1336.     If the command table for a given menu is less than the number of items in the menu,
  1337.     and the command being enabled is the last item in the command table, then all
  1338.     items from there on down are also enabled.  This is useful for menus that get
  1339.     appended to, such as the desk accessory list, font list, or speaking voices list.
  1340. */
  1341. {
  1342.     short    whichMenu;
  1343.     short    whichItem;
  1344.     
  1345.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1346.         {
  1347.         short        i;
  1348.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1349.         
  1350.         if (menu)
  1351.             {
  1352.             short        numItems = CountMItems(menu);
  1353.             
  1354.             for (i = whichItem; i <= numItems; ++i)
  1355.                 EnableItem(menu, i);
  1356.             }
  1357.         }
  1358.     else
  1359.         {
  1360.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1361.  
  1362.         if (menu)
  1363.             EnableItem(menu, whichItem);
  1364.         }
  1365.         
  1366. } // EnableCommand
  1367.  
  1368. // --------------------------------------------------------------------------------------------------------------
  1369. #pragma segment Main
  1370.  
  1371. void ChangeCommandName(short commandID, short resourceID, short resourceIndex)
  1372. {
  1373.     short        whichMenu;
  1374.     short        whichItem;
  1375.     MenuHandle    menu;
  1376.     
  1377.     // figure out how this command maps into the menu bar
  1378.     CommandToIDs(commandID, &whichMenu, &whichItem);
  1379.     menu = GetMenuHandle(whichMenu);
  1380.     
  1381.     // then make this item into the requested new string
  1382.     {
  1383.     Str255        theString;
  1384.     
  1385.     GetIndString(theString, resourceID, resourceIndex);
  1386.     SetMenuItemText(menu, whichItem, theString);
  1387.     }
  1388.     
  1389. } // ChangeCommandName
  1390.  
  1391. // --------------------------------------------------------------------------------------------------------------
  1392. #pragma segment Main
  1393.  
  1394. void EnableCommandCheck(short commandID, Boolean check)
  1395. {
  1396.  
  1397.     short    whichMenu;
  1398.     short    whichItem;
  1399.     
  1400.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1401.         {
  1402.         short        i;
  1403.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1404.         short        numItems = CountMItems(menu);
  1405.         
  1406.         for (i = whichItem; i <= numItems; ++i)
  1407.             {
  1408.             EnableItem(menu, i);
  1409.             CheckItem(menu, i, check);
  1410.             }
  1411.         }
  1412.     else
  1413.         {
  1414.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1415.  
  1416.         EnableItem(menu, whichItem);
  1417.         CheckItem(menu, whichItem, check);
  1418.         }
  1419.         
  1420. } // EnableCommandCheck
  1421.  
  1422.  
  1423. // --------------------------------------------------------------------------------------------------------------
  1424. #pragma segment Main
  1425.  
  1426. void EnableCommandCheckStyle(short commandID, Boolean check, short style)
  1427. {
  1428.  
  1429.     short    whichMenu;
  1430.     short    whichItem;
  1431.     
  1432.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1433.         {
  1434.         short        i;
  1435.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1436.         short        numItems = CountMItems(menu);
  1437.         
  1438.         for (i = whichItem; i <= numItems; ++i)
  1439.             {
  1440.             EnableItem(menu, i);
  1441.             CheckItem(menu, i, check);
  1442.             SetItemStyle(menu, i, style);
  1443.             }
  1444.         }
  1445.     else
  1446.         {
  1447.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1448.  
  1449.         EnableItem(menu, whichItem);
  1450.         CheckItem(menu, whichItem, check);
  1451.         SetItemStyle(menu, whichItem, style);
  1452.         }
  1453.         
  1454. } // EnableCommandCheckStyle
  1455.  
  1456. // --------------------------------------------------------------------------------------------------------------
  1457. #pragma segment Main
  1458.  
  1459. void AdjustMenus(WindowRef pWindow, Boolean editDialogs, Boolean forceTitlesOn)
  1460. {
  1461.     Boolean                 wasEnabled[mNumberMenus];    // Old state of menus
  1462.     short                    whichMenu;                    // for stepping through menus
  1463.     MenuHandle                menu;                        // for reading in menu IDs
  1464.     WindowDataPtr             pData = GetWindowInfo(pWindow);
  1465.     
  1466.     // Step through all of the menus 
  1467.     for (whichMenu = mApple; whichMenu <= mLastMenu; whichMenu++)
  1468.         {
  1469.         // Save the old state of the menu title 
  1470.         menu = GetMenuHandle(whichMenu);
  1471.         if (menu)                                // because contents menu may not be around
  1472.             {
  1473.             if (forceTitlesOn)                
  1474.                 wasEnabled[mLastMenu - whichMenu] = false;
  1475.             else
  1476.                 wasEnabled[mLastMenu - whichMenu] = (((**menu).enableFlags && 1) == 1);
  1477.             
  1478.             // Disable the entire menu 
  1479.             (**menu).enableFlags = 0;        
  1480.             }
  1481.         }
  1482.     
  1483.     // select all, unless someone else changes it
  1484.     ChangeCommandName(cSelectAll, kMiscStrings, iSelectAllCommand);
  1485.  
  1486.     // if we have NO windows, or the current window is one we understand
  1487.     if ((pWindow == nil) || (pData))
  1488.         {
  1489.         // enable the default commands
  1490.         EnableCommand(cAbout);
  1491.         EnableCommand(cDeskAccessory);
  1492.         
  1493.         EnableCommand(cNew);
  1494.         EnableCommand(cOpen);
  1495.         EnableCommand(cQuit);
  1496.     
  1497.         EnableCommand(cShowClipboard);
  1498.         }
  1499.     else
  1500.         {
  1501.         // it's printing or a dialog, so enable cut/copy/paste
  1502.         if (editDialogs)
  1503.             {
  1504.             EnableCommand(cCut);
  1505.             EnableCommand(cCopy);
  1506.             EnableCommand(cPaste);
  1507.             EnableCommand(cClear);
  1508.             }
  1509.         
  1510.         // and desk accs too!        
  1511.         EnableCommand(cDeskAccessory);
  1512.  
  1513.         }
  1514.         
  1515.     if ( (pWindow) && (pData) )
  1516.         {
  1517.         // all windows can be closed
  1518.         if (FrontWindow())
  1519.             EnableCommand(cClose);
  1520.  
  1521.         // changed documents can be saved, but only if the file is open for write
  1522.         if (     (pData->changed) && 
  1523.                 ((pData->isWritable) || (pData->dataRefNum == -1)) )
  1524.             EnableCommand(cSave);
  1525.         
  1526.         // objects with a print method can be printed and page setup-ed
  1527.         if (pData->pPrintPage)
  1528.             {
  1529.             EnableCommand(cPrint);
  1530.             EnableCommand(cPageSetup);
  1531.             EnableCommand(cPrintOneCopy);
  1532.             }
  1533.             
  1534.         // let object enable anything else that needs to be enabled
  1535.         if (pData->pAdjustMenus)
  1536.             (*(pData->pAdjustMenus)) (pWindow, pData);
  1537.         }
  1538.         
  1539.     // Now determine if any of the menus have changed state
  1540.     {
  1541.     Boolean gotToRedraw = false;
  1542.     
  1543.     for (whichMenu = mApple; whichMenu <= mLastMenu; ++whichMenu)
  1544.         {
  1545.         menu = GetMenuHandle(whichMenu);
  1546.     
  1547.         if (menu)        // because contents menu may not be around
  1548.             {
  1549.             // If any of the menu is enabled 
  1550.             if ((**menu).enableFlags != 0)
  1551.                 {
  1552.                 // Make sure to turn on the menu title 
  1553.                 (**menu).enableFlags |= 1;
  1554.                 }
  1555.                 
  1556.             /*     If this new state is different than the saved state, then the menu bar
  1557.                 will need to be redrawn */
  1558.             if (wasEnabled[mLastMenu - whichMenu] != ((**menu).enableFlags && 1))
  1559.                 {
  1560.                 gotToRedraw = true;
  1561.                 }
  1562.             }
  1563.         }
  1564.         
  1565.     // And if any titles have changed state, redraw them 
  1566.     if (gotToRedraw)
  1567.         DrawMenuBar();
  1568.     }
  1569.         
  1570. } // AdjustMenus
  1571.  
  1572. // --------------------------------------------------------------------------------------------------------------
  1573. // PRINTING UTILITY ROUTINES
  1574. // --------------------------------------------------------------------------------------------------------------
  1575. #pragma segment Printing
  1576.  
  1577. Boolean IsSomewhereInRectangle(gxRectangle *pContainer, gxRectangle *pShape)
  1578. /*
  1579.     Calculates this by saying the rectangle doesn't intersect at ALL,
  1580.     and then NOTs that expression.
  1581. */
  1582. {
  1583.     return
  1584.         (!(
  1585.         pShape->top > pContainer->bottom ||
  1586.         pShape->bottom < pContainer->top ||
  1587.         pShape->left > pContainer->right ||
  1588.         pShape->right < pContainer->left
  1589.         ));
  1590.         
  1591. } // IsSomewhereInRectangle
  1592.  
  1593. // --------------------------------------------------------------------------------------------------------------
  1594. #pragma segment Printing
  1595.     
  1596. static OSErr SimpleCatchShape(gxShape newShape, CatchRefCon * pRefCon)
  1597. {
  1598.     Boolean                addShape = false;
  1599.     gxGraphicsError        anErr;
  1600.  
  1601.     // did the user abort printing?
  1602.     anErr = GXGetJobError(pRefCon->theJob);
  1603.     if (anErr != noErr)
  1604.         return anErr;
  1605.         
  1606.     switch (GXGetShapeType(newShape))
  1607.         {
  1608.         
  1609.         // if we have a layout, turn off justification so that we can get
  1610.         // better looking morph effects, and also enable the default features
  1611.         // of the layout.  However, we can only do this if the layout is
  1612.         // in a script system where the translation wasn't done by a Print Action Hook.
  1613.         // For now, this means only smRoman styles.
  1614.         case gxLayoutType:
  1615.             if (pRefCon->doLayout)
  1616.                 {
  1617.                 Boolean            enableLayout = pRefCon->doLayout;
  1618.         
  1619.                 gxStyle            * theStyles;
  1620.                 long            styleCount, index;
  1621.                 
  1622.                 GXGetLayout(newShape, nil,
  1623.                             &styleCount, nil, nil,    // style runs
  1624.                             nil, nil, nil,    // run levels
  1625.                             nil, nil);
  1626.                 theStyles = (gxStyle*) NewPtr(sizeof(gxStyle)*styleCount);
  1627.                 if (theStyles)
  1628.                     {
  1629.                     GXGetLayout(newShape, nil,
  1630.                                 &styleCount, nil, theStyles,    // style runs
  1631.                                 nil, nil, nil,    // run levels
  1632.                                 nil, nil);
  1633.                     
  1634.                     enableLayout = true;
  1635.                     for (index = 0; index < styleCount; ++index)
  1636.                         {
  1637.                         gxFontScript theScript;
  1638.                         gxFontPlatform thePlatform = GXGetStyleEncoding(theStyles[index], &theScript, nil);
  1639.                         
  1640.                         if ((thePlatform != gxMacintoshPlatform) || (theScript != gxRomanScript))
  1641.                             enableLayout = false;
  1642.                         }
  1643.                             
  1644.                     if (enableLayout)
  1645.                         for (index = 0; index < styleCount; ++index)
  1646.                             {
  1647.                             gxRunControls theControls;
  1648.                             
  1649.                             // re-enable run control features
  1650.                             GXGetStyleRunControls(theStyles[index], &theControls);
  1651.                             theControls.flags = 0;
  1652.                             theControls.track = 0;
  1653.                             theControls.hangingInhibitFactor = 0;
  1654.                             theControls.kerningInhibitFactor = 0;
  1655.                             GXSetStyleRunControls(theStyles[index], &theControls);
  1656.                             
  1657.                             // and turn back on default features
  1658.                             GXSetStyleRunFeatures(theStyles[index], 0, nil);
  1659.                             }
  1660.                         
  1661.                     DisposePtr((Ptr) theStyles);
  1662.                     }
  1663.         
  1664.                 if ( (enableLayout) && (GetSysDirection() == 0) )
  1665.                     {
  1666.                     // turn off justification
  1667.                     {
  1668.                     gxLayoutOptions    layoutOptions;
  1669.             
  1670.                     // get the current layout options
  1671.                     layoutOptions.baselineRec = nil;
  1672.                     GXGetLayout(newShape, nil,
  1673.                                 nil, nil, nil,    // style runs
  1674.                                 nil, nil, nil,    // run levels
  1675.                                 &layoutOptions, nil);
  1676.                         
  1677.                     // setting width to zero allows lines to float, but causes multi-styled
  1678.                     // lines (separate layouts from the translation process) to run into
  1679.                     // one another, so we can't do that
  1680.                     //layoutOptions.width = 0;
  1681.                     layoutOptions.just = gxNoJustification;
  1682.                     GXSetLayout(newShape, 
  1683.                                 0, nil, nil,    // text runs
  1684.                                 0, nil, nil,    // style runs
  1685.                                 0, nil, nil, // run levels
  1686.                                 &layoutOptions, nil);
  1687.                     }
  1688.                     
  1689.                     // un-clip the shape left and right so we can see things like hanging puncs.
  1690.                         {
  1691.                         gxShape            newClip;
  1692.                         gxRectangle        bounds;
  1693.                         
  1694.                         newClip = GXGetShapeClip(newShape);
  1695.                         GXGetShapeLocalBounds(newClip, &bounds);
  1696.                         GXDisposeShape(newClip);
  1697.                         
  1698.                         bounds.left     = gxNegativeInfinity;
  1699.                         bounds.right     = gxPositiveInfinity;
  1700.                         newClip = GXNewRectangle(&bounds);
  1701.                         GXSetShapeClip(newShape, newClip);
  1702.                         GXDisposeShape(newClip);
  1703.                         }
  1704.                     }
  1705.                 
  1706.                 }
  1707.  
  1708.             // if we aren't forming layouts, we'll go ahead and check for this being on
  1709.             // the shape.  But for pure text, we'll just always add the text.  TextEdit
  1710.             // is pretty good about pre-clipping for us.
  1711.             if (!pRefCon->doLayout)
  1712.                 {
  1713.                 gxRectangle    bounds;
  1714.                 
  1715.                 GXGetShapeLocalBounds(newShape, &bounds);
  1716.                 if (IsSomewhereInRectangle(&pRefCon->thePageRectangle, &bounds))
  1717.                     addShape = true;
  1718.                 }
  1719.             else
  1720.                 addShape = true;
  1721.             break;
  1722.         
  1723.         // never add these for text case
  1724.         case gxRectangleType:
  1725.             if (!pRefCon->doLayout)
  1726.                 addShape = true;
  1727.             break;
  1728.             
  1729.         // always add these shapes if we see any because we don't know how to filter them
  1730.         case gxEmptyType:
  1731.         case gxFullType:
  1732.         case gxPictureType:
  1733.             addShape = true;
  1734.             break;
  1735.             
  1736.         default:
  1737.             {
  1738.             gxRectangle    bounds;
  1739.             
  1740.             GXGetShapeLocalBounds(newShape, &bounds);
  1741.             if (IsSomewhereInRectangle(&pRefCon->thePageRectangle, &bounds))
  1742.                 addShape = true;
  1743.             }
  1744.             break;
  1745.             
  1746.         } // switch
  1747.         
  1748.     if (addShape)
  1749.         GXSetPictureParts(pRefCon->thePage, 0, 0, 1, &newShape, nil, nil, nil);    /* Add shape */
  1750.     
  1751.     GXGetGraphicsError(&anErr);
  1752.     
  1753.     if (anErr == noErr)
  1754.         {
  1755.         GXIdleJob(pRefCon->theJob);
  1756.         anErr = GXGetJobError(pRefCon->theJob);
  1757.         }
  1758.         
  1759.     return anErr;
  1760.     
  1761. } // SimpleCatchShape
  1762.  
  1763. #if GENERATINGCFM
  1764.     static RoutineDescriptor gSimpleCatchShapeRD = BUILD_ROUTINE_DESCRIPTOR(uppgxShapeSpoolProcInfo, SimpleCatchShape);
  1765.     static gxShapeSpoolUPP gSimpleCatchShape = &gSimpleCatchShapeRD;
  1766. #else
  1767.     static gxShapeSpoolUPP gSimpleCatchShape = NewgxShapeSpoolProc(SimpleCatchShape);
  1768. #endif
  1769.  
  1770. // --------------------------------------------------------------------------------------------------------------
  1771. #pragma segment Printing
  1772.  
  1773. static OSErr CompleteSpoolFileMessage( gxSpoolFile theSpoolFile )
  1774. {
  1775.     OSErr            anErr         = noErr;
  1776.     Handle            hIcon;
  1777.     short            sourceIcon     = 0;
  1778.     short            docIcon     = 132;
  1779.     WindowDataPtr    pData         = GXGetJobRefCon(GXGetJob());
  1780.     
  1781.     // for some file types, we can supply a nicer icon for the Finder to display
  1782.     // within the queue when this document is printing
  1783.     switch (pData->originalFileType)
  1784.         {
  1785.         case 'TEXT':
  1786.         case 'sEXT':
  1787.             sourceIcon = kTextIcon;
  1788.             break;
  1789.                         
  1790.         case 'ttro':
  1791.             sourceIcon = kReadOnlyIcon;
  1792.             break;
  1793.  
  1794.         case 'PICT':
  1795.             sourceIcon = kPICTIcon;
  1796.             break;
  1797.         }
  1798.         
  1799.     if (sourceIcon != 0)
  1800.         {
  1801.         hIcon = GetResource('ICN#', sourceIcon);
  1802.         if (hIcon != nil) 
  1803.             {
  1804.             HNoPurge(hIcon);
  1805.             DetachResource(hIcon);
  1806.             anErr = Send_GXSpoolResource (theSpoolFile, hIcon, 'ICN#', docIcon);
  1807.             }
  1808.         nrequire(anErr, SpoolOneBit);
  1809.         
  1810.         hIcon = GetResource('icl4', sourceIcon);
  1811.         if (hIcon != nil) 
  1812.             {
  1813.             HNoPurge(hIcon);
  1814.             DetachResource(hIcon);
  1815.             anErr = Send_GXSpoolResource (theSpoolFile, hIcon, 'icl4', docIcon);
  1816.             }
  1817.         nrequire(anErr, SpoolFourBit);
  1818.     
  1819.         hIcon = GetResource('icl8', sourceIcon);
  1820.         if (hIcon != nil) 
  1821.             {
  1822.             HNoPurge(hIcon);
  1823.             DetachResource(hIcon);
  1824.             anErr = Send_GXSpoolResource (theSpoolFile, hIcon, 'icl8', docIcon);
  1825.             }
  1826.         nrequire(anErr, SpoolEightBit);
  1827.         }
  1828.     
  1829.     anErr = Forward_GXCompleteSpoolFile( theSpoolFile );
  1830.     
  1831. // FALL THROUGH EXCEPTION HANDLING
  1832. SpoolOneBit:
  1833. SpoolFourBit:
  1834. SpoolEightBit:
  1835.  
  1836.     return anErr;
  1837.     
  1838. } // CompleteSpoolFileMessage
  1839.  
  1840. #if GENERATINGCFM
  1841.     static RoutineDescriptor gCompleteSpoolFileMessageRD = BUILD_ROUTINE_DESCRIPTOR(uppGXCompleteSpoolFileProcInfo, CompleteSpoolFileMessage);
  1842.     static GXCompleteSpoolFileUPP gCompleteSpoolFileMessage = &gCompleteSpoolFileMessageRD;
  1843. #else
  1844.     static GXCompleteSpoolFileUPP gCompleteSpoolFileMessage = NewGXCompleteSpoolFileProc(CompleteSpoolFileMessage);
  1845. #endif
  1846.  
  1847. // --------------------------------------------------------------------------------------------------------------
  1848. #pragma segment Printing
  1849.  
  1850. static OSErr PrintingEventMessage(EventRecord *event, Boolean filter)
  1851. {
  1852.     OSErr     anErr = noErr;
  1853.     GrafPtr    curPort;
  1854.     
  1855.     GetPort(&curPort);
  1856.     if (filter == false) 
  1857.         {
  1858.         switch ( event->what ) 
  1859.             {
  1860.             case mouseDown:
  1861.             case keyDown:
  1862.             case autoKey:
  1863.                 break;
  1864.                 
  1865.             case activateEvt:
  1866.             case updateEvt:
  1867.             default:
  1868.                 HandleEvent(event);
  1869.                 break;
  1870.             }
  1871.         }
  1872.     
  1873.     anErr = Forward_GXPrintingEvent(event, filter);
  1874.     SetPort(curPort);
  1875.     
  1876.     return anErr;
  1877.     
  1878. } // PrintingEventMessage
  1879.  
  1880. #if GENERATINGCFM
  1881.     static RoutineDescriptor gPrintingEventMessageRD = BUILD_ROUTINE_DESCRIPTOR(uppGXPrintingEventProcInfo, PrintingEventMessage);
  1882.     static GXPrintingEventUPP gPrintingEventMessage = &gPrintingEventMessageRD;
  1883. #else
  1884.     static GXPrintingEventUPP gPrintingEventMessage = NewGXPrintingEventProc(PrintingEventMessage);
  1885. #endif
  1886.  
  1887.  
  1888.  
  1889. // --------------------------------------------------------------------------------------------------------------
  1890. // FILE UTILITY ROUTINES
  1891. // --------------------------------------------------------------------------------------------------------------
  1892. #pragma segment Main
  1893.  
  1894. static Boolean BringToFrontIfOpen(FSSpecPtr pSpec)
  1895. {
  1896.     WindowRef        pWindow;
  1897.     
  1898.     pWindow = FrontWindow();
  1899.     while (pWindow)
  1900.         {
  1901.         WindowDataPtr pData = GetWindowInfo(pWindow);
  1902.         
  1903.         if (
  1904.             (pData) &&
  1905.             (pData->fileSpec.vRefNum == pSpec->vRefNum) &&
  1906.             (pData->fileSpec.parID == pSpec->parID) &&
  1907.             EqualString(pData->fileSpec.name, pSpec->name, false, false)
  1908.             )
  1909.             {
  1910.             SelectWindow(pWindow);
  1911.             return true;
  1912.             }
  1913.             
  1914.         pWindow = GetNextWindow(pWindow);
  1915.         }
  1916.         
  1917.     return false;
  1918.     
  1919. } // BringToFrontIfOpen
  1920.  
  1921. // --------------------------------------------------------------------------------------------------------------
  1922. #pragma segment Main
  1923.  
  1924. static Boolean BringToFrontIfExists(ResType windowKind)
  1925. {
  1926.     WindowRef        pWindow;
  1927.     
  1928.     pWindow = FrontWindow();
  1929.     while (pWindow)
  1930.         {
  1931.         WindowDataPtr pData = GetWindowInfo(pWindow);
  1932.         
  1933.         if ((pData) && (pData->windowKind == windowKind))
  1934.             {
  1935.             SelectWindow(pWindow);
  1936.             return true;
  1937.             }
  1938.             
  1939.         pWindow = GetNextWindow(pWindow);
  1940.         }
  1941.         
  1942.     return false;
  1943.     
  1944. } // BringToFrontIfExists
  1945.  
  1946. // --------------------------------------------------------------------------------------------------------------
  1947. // MAIN SIMPLETEXT ROUTINES
  1948. // --------------------------------------------------------------------------------------------------------------
  1949. #pragma segment Main
  1950.  
  1951. static OSErr MakeNewWindow(ResType windowKind, FSSpecPtr fileSpec, OSType fileType, Boolean *pWasAlreadyOpen)
  1952. {
  1953.     OSErr                anErr = fnfErr;
  1954.     PreflightRecord        thePreflight;
  1955.     PreflightWindowProc    pPreflight = nil;
  1956.     WindowRef            pWindow;
  1957.     WindowDataPtr        pData;
  1958.     
  1959.     // require a certain amount of RAM free before we allow the new window to be created
  1960.     if (FreeMem() < kRAMNeededForNew)
  1961.         anErr = memFullErr;
  1962.         
  1963.     // <50> if we already have a document open from this file, bring the window to the
  1964.     // front and return with no error
  1965.     if ( (fileSpec) && (fileType != 'sEXT') && (BringToFrontIfOpen(fileSpec)) )
  1966.         {
  1967.         if (pWasAlreadyOpen)
  1968.             *pWasAlreadyOpen = true;
  1969.         anErr = noErr;
  1970.         return(anErr);
  1971.         }
  1972.     if (pWasAlreadyOpen)
  1973.         *pWasAlreadyOpen = false;
  1974.     if (anErr != fnfErr)
  1975.         {
  1976.         nrequire(anErr, SanityCheckFailed);
  1977.         }
  1978.         
  1979.     // initialize our behavior
  1980.     thePreflight.continueWithOpen     = true;
  1981.     thePreflight.resourceID         = kDefaultWindowID;
  1982.     thePreflight.wantHScroll         = false;
  1983.     thePreflight.wantVScroll         = false;
  1984.     thePreflight.storageSize         = sizeof(WindowDataRecord);
  1985.     thePreflight.makeProcPtr         = nil;
  1986.     thePreflight.openKind            = fsRdPerm;
  1987.     thePreflight.needResFork        = false;
  1988.     thePreflight.doZoom                = false;
  1989.     thePreflight.fileType            = fileType;
  1990.     
  1991.     switch (windowKind)
  1992.         {
  1993.         case kAboutWindow:
  1994.             pPreflight = AboutPreflightWindow;
  1995.             break;
  1996.  
  1997.         case kPICTWindow:
  1998.             pPreflight = PICTPreflightWindow;
  1999.             break;
  2000.  
  2001.         case kMovieWindow:
  2002.             pPreflight = MoviePreflightWindow;
  2003.             break;
  2004.  
  2005.         case kClipboardWindow:
  2006.             pPreflight = ClipboardPreflightWindow;
  2007.             break;
  2008.  
  2009.         case kTextWindow:
  2010.             pPreflight = TextPreflightWindow;
  2011.             break;
  2012.  
  2013.         case kGXWindow:
  2014.             pPreflight = GXPreflightWindow;
  2015.             break;
  2016.  
  2017.         case kThreeDWindow:
  2018.             pPreflight = ThreeDPreflightWindow;
  2019.             break;
  2020.         }
  2021.     
  2022.     // preflight the window    
  2023.     if (pPreflight)
  2024.         anErr = (*pPreflight) (&thePreflight);
  2025.     nrequire(anErr, PreflightFailed);
  2026.     
  2027.     if (thePreflight.continueWithOpen)
  2028.         {
  2029.         // allocate a place for the window
  2030.         pData = (WindowDataPtr)NewPtrClear(thePreflight.storageSize);
  2031.         anErr = MemError();
  2032.         nrequire(anErr, FailedToAllocateWindow);
  2033.         
  2034.         // then actually create the window
  2035.         if (gMachineInfo.theEnvirons.hasColorQD)
  2036.             pWindow = (WindowRef)GetNewCWindow(thePreflight.resourceID, pData, (WindowPtr)-1);
  2037.         else
  2038.             pWindow = (WindowRef)GetNewWindow(thePreflight.resourceID, pData, (WindowPtr)-1);
  2039.         if (!pWindow) anErr = memFullErr;
  2040.         nrequire(anErr, NewWindowFailed);
  2041.         SetWRefCon(pWindow, (long) pData);
  2042.                 
  2043.         // zoom the rectangle to big size on this monitor 
  2044.         // based upon which scroll bars they want
  2045.         {
  2046.         Rect    rect = GetWindowPort(pWindow)->portRect;
  2047.         Rect    bigRect;
  2048.         
  2049.         if (gMachineInfo.theEnvirons.hasColorQD)
  2050.             bigRect = (**GetMainDevice()).gdRect;
  2051.         else
  2052.             bigRect = qd.screenBits.bounds;
  2053.         bigRect.top += GetMBarHeight() * 2;
  2054.         bigRect.left += 4;
  2055.         bigRect.bottom -= 4;
  2056.         bigRect.right -= 65;
  2057.         
  2058.         SetPort((GrafPtr) GetWindowPort(pWindow));
  2059.         LocalToGlobal(&TopLeft(rect));
  2060.         LocalToGlobal(&BotRight(rect));
  2061.         
  2062.         if ( (thePreflight.wantHScroll) || (thePreflight.doZoom) )
  2063.             {
  2064. //            rect.left = bigRect.left;
  2065.             rect.right = bigRect.right;
  2066.             }
  2067.             
  2068.         if ( (thePreflight.wantVScroll) || (thePreflight.doZoom) )
  2069.             {
  2070. //            rect.top = bigRect.top;
  2071.             rect.bottom = bigRect.bottom;
  2072.             }
  2073.         
  2074.         MoveWindow(pWindow, rect.left, rect.top, false);
  2075.         SizeWindow(pWindow, rect.right - rect.left, rect.bottom - rect.top, false);
  2076.         }
  2077.         
  2078.         // fill in the default contents of the window
  2079.         pData->windowKind         = windowKind;
  2080.         pData->originalFileType    = fileType;
  2081.         pData->pMakeWindow         = (MakeWindowProc)thePreflight.makeProcPtr;
  2082.         pData->resRefNum        = -1;
  2083.         pData->dataRefNum        = -1;
  2084.         pData->contentRect         = GetWindowPort(pWindow)->portRect;
  2085.         
  2086.         // make the scroll bars
  2087.         {
  2088.         Rect    controlRect;
  2089.         
  2090.         if (thePreflight.wantHScroll)
  2091.             {
  2092.             pData->contentRect.bottom -= kScrollBarSize;
  2093.             controlRect = GetWindowPort(pWindow)->portRect;
  2094.             controlRect.top = controlRect.bottom - 16;
  2095.             if (thePreflight.wantVScroll)
  2096.                 controlRect.right -= kGrowScrollAdjust;
  2097.             OffsetRect(&controlRect, -1, 1);
  2098.             pData->hScroll = NewControl(pWindow, &controlRect, "\p", true, 0, 0, 0, scrollBarProc, 0);
  2099.             }
  2100.         if (thePreflight.wantVScroll)
  2101.             {
  2102.             pData->contentRect.right -= kScrollBarSize;
  2103.             controlRect = GetWindowPort(pWindow)->portRect;
  2104.             controlRect.left = controlRect.right - 16;
  2105.             if (thePreflight.wantVScroll)
  2106.                 controlRect.bottom -= kGrowScrollAdjust;
  2107.             OffsetRect(&controlRect, 1, -1);
  2108.             pData->vScroll = NewControl(pWindow, &controlRect, "\p", true, 0, 0, 0, scrollBarProc, 0);
  2109.             }
  2110.         }
  2111.  
  2112.         // got a name?  Open the file        
  2113.         if (fileSpec)
  2114.             {
  2115.             anErr = FSpOpenDF(fileSpec, thePreflight.openKind, &pData->dataRefNum);
  2116.             if     ( 
  2117.                     ((anErr == afpAccessDenied) || (anErr == opWrErr) || (anErr == permErr) ) && 
  2118.                     (thePreflight.openKind != fsRdPerm)
  2119.                 )
  2120.                 {
  2121.                 thePreflight.openKind = fsRdPerm;
  2122.                 pData->isWritable = false;
  2123.                 anErr = FSpOpenDF(fileSpec, thePreflight.openKind, &pData->dataRefNum);
  2124.                 }
  2125.             else
  2126.                 pData->isWritable = true;
  2127.             nrequire(anErr, FailedToOpenFile);
  2128.  
  2129.             // okay not to find a resource fork, because some don't have them                
  2130.             pData->resRefNum = FSpOpenResFile(fileSpec, thePreflight.openKind);
  2131.             
  2132.             // save the file spec in case someone is interested
  2133.             pData->fileSpec = *fileSpec;
  2134.             }
  2135.             
  2136.         if (pData->pMakeWindow)
  2137.             {
  2138.             Rect oldContent = pData->contentRect;
  2139.             anErr = (*(pData->pMakeWindow)) (pWindow, pData);
  2140.             if (!EqualRect(&oldContent, &pData->contentRect))
  2141.                 {
  2142.                 SizeWindow(pWindow, 
  2143.                         pData->contentRect.right  + (pData->vScroll != 0) * kScrollBarSize,
  2144.                         pData->contentRect.bottom + (pData->hScroll != 0) * kScrollBarSize,
  2145.                         false);
  2146.                 }
  2147.             }
  2148.         nrequire(anErr, FailedMakeWindow);
  2149.  
  2150.         // got a name?  Use it as the window title
  2151.         if ( (fileSpec) && (!pData->openAsNew) )
  2152.             SetWTitle(pWindow, fileSpec->name);
  2153.         else
  2154.             {
  2155.             if ((gMachineInfo.documentCount == 1) && (pData->windowKind == kTextWindow))
  2156.                 {
  2157.                 Str255 tempString;
  2158.         
  2159.                 GetIndString(tempString, kMiscStrings, iFirstNewDocumentTitle);    // get the "untitled" string (no number)
  2160.                 SetWTitle(pWindow, tempString);
  2161.                 }
  2162.             else
  2163.                 {
  2164.                 Str255    tempString;
  2165.                 Str32    numString;
  2166.     
  2167.                 GetWTitle(pWindow, tempString);
  2168.                 NumToString(gMachineInfo.documentCount, numString);
  2169.                 (void) ZeroStringSub(tempString, numString);
  2170.                 SetWTitle(pWindow, tempString);
  2171.                 }
  2172.  
  2173.             if (pData->bumpUntitledCount)
  2174.                 gMachineInfo.documentCount++;    // bump count if appropriate for this kind of document
  2175.             }
  2176.  
  2177.         // Make sure the scroll bars are reasonable in size, and move if they must
  2178.         AdjustScrollBars(pWindow, true, true, nil);
  2179.  
  2180.         // finally, if all goes well, we can see the window itself!
  2181.         ShowWindow(pWindow);
  2182.         }
  2183.  
  2184.     return noErr;
  2185.  
  2186. // EXCEPTION HANDLING
  2187. FailedMakeWindow:
  2188.     if (pData->resRefNum != -1)
  2189.         CloseResFile(pData->resRefNum);
  2190.     if (pData->dataRefNum != -1)
  2191.         FSClose(pData->dataRefNum);
  2192.     
  2193. FailedToOpenFile:
  2194.     CloseWindow(pWindow);
  2195.     
  2196. NewWindowFailed:
  2197.     DisposePtr((Ptr)pData);
  2198.     
  2199. FailedToAllocateWindow:
  2200. PreflightFailed:
  2201. SanityCheckFailed:
  2202.     return anErr;
  2203.     
  2204. } // MakeNewWindow
  2205.  
  2206. // --------------------------------------------------------------------------------------------------------------
  2207. #pragma segment Main
  2208.  
  2209.  
  2210. #define kVisualDelay 8
  2211.  
  2212. static pascal Boolean SaveChangesFilter(DialogRef theDialog, EventRecord *theEvent, short *itemHit)
  2213. {
  2214.     if (StdFilterProc(theDialog, theEvent, itemHit))
  2215.         return true;
  2216.  
  2217.     if (theEvent->what == updateEvt)
  2218.         {
  2219.         HandleEvent(theEvent);
  2220.         }
  2221.  
  2222.     if (theEvent->what == keyDown)
  2223.         {
  2224.         StringPtr keyEquivs = *GetString(kSaveChangesWindowID);
  2225.         unsigned char theKey = theEvent->message & charCodeMask;
  2226.  
  2227.         if (keyEquivs && (theKey == keyEquivs[1] || theKey == keyEquivs[2]))
  2228.             {
  2229.             short itemType;
  2230.             Rect theRect;
  2231.             ControlRef theControl;
  2232.             long finalTicks;
  2233.  
  2234.             GetDialogItem(theDialog, dontSaveChanges, &itemType, (Handle *) &theControl, &theRect);
  2235.             HiliteControl(theControl, kControlButtonPart);
  2236.             Delay(kVisualDelay, &finalTicks);
  2237.             HiliteControl(theControl, 0);
  2238.  
  2239.             *itemHit = dontSaveChanges;
  2240.             return true;
  2241.             }
  2242.         }
  2243.  
  2244.     return false;
  2245. }
  2246.  
  2247.  
  2248. static OSErr DoCloseWindow(WindowRef pWindow, Boolean quitting)
  2249. {
  2250.     OSErr            anErr = noErr;
  2251.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2252.     
  2253.     if ( (pData) && (pData->changed) )
  2254.         {
  2255.         short         hit;
  2256.         Str255        wTitle;
  2257.         DialogRef    dPtr;
  2258.         
  2259.         GetWTitle(pWindow, wTitle);
  2260.         SetCursor(&qd.arrow);
  2261.         ParamText(wTitle, "\p", "\p", "\p");
  2262.         
  2263.         if (gMachineInfo.haveNavigationServices)
  2264.         {
  2265.             hit = ConfirmSaveDialog(wTitle, quitting, MyEventProc);
  2266.         }
  2267.         else
  2268.         {
  2269.             hit = cancel;
  2270.             dPtr = GetNewDialog(kSaveChangesWindowID, nil, (WindowRef)-1);
  2271.             if (dPtr)
  2272.                 {
  2273.                 SetDialogDefaultItem(dPtr, ok);
  2274.                 SetDialogCancelItem (dPtr, cancel);
  2275.                 BeginMovableModal();
  2276.                 do
  2277.                     {
  2278.                     MovableModalDialog(SaveChangesFilter, &hit);
  2279.                     } while ((hit != dontSaveChanges) && (hit != ok) && (hit != cancel));
  2280.                     
  2281.                 DisposeDialog(dPtr);
  2282.                 EndMovableModal();
  2283.                 }
  2284.         }
  2285.     
  2286.         switch (hit)
  2287.             {
  2288.             case ok:
  2289.                 anErr = DoCommand(pWindow, cSave, 0);
  2290.                 if (anErr == eUserCanceled)        // redundant?
  2291.                     gAllDone = false;
  2292.                 break;
  2293.                 
  2294.             case cancel:
  2295.                 anErr = eUserCanceled;
  2296.                 gAllDone = false;
  2297.                 break;
  2298.                 
  2299.             case dontSaveChanges:
  2300.                 // don't save, so just close it
  2301.                 break;
  2302.             }
  2303.         }
  2304.  
  2305.     if (anErr == noErr)
  2306.         {
  2307.         if ( (pData) && (pData->pCloseWindow) )
  2308.             {
  2309.             // let the object close the window if it wishes to
  2310.             anErr = (*(pData->pCloseWindow)) (pWindow, pData);
  2311.             }
  2312.  
  2313.         // otherwise we close it the default way
  2314.         if (anErr == noErr)
  2315.             {
  2316.             if (pData)
  2317.                 {
  2318.                 CloseWindow(pWindow);
  2319.     
  2320.                 if (pData->hPrint)
  2321.                     {
  2322.                     if (gMachineInfo.haveGX)
  2323.                         GXDisposeJob( pData->hPrint);
  2324.                     else
  2325.                         DisposeHandle((Handle) pData->hPrint);
  2326.                     }
  2327.                     
  2328.                 if (pData->resRefNum != -1)
  2329.                     CloseResFile(pData->resRefNum);
  2330.                 if (pData->dataRefNum != -1)
  2331.                     FSClose(pData->dataRefNum);
  2332.                 DisposePtr((Ptr) pData);
  2333.                 }
  2334.             }
  2335.         }
  2336.  
  2337.     // If we closed the last window, clean up
  2338.     if (FrontWindow() == nil)
  2339.         {
  2340.         AdjustMenus(nil, true, false);
  2341.         gMachineInfo.documentCount = 1;        // back to "untitled"
  2342.         }
  2343.     
  2344.     return anErr;
  2345.     
  2346. } // DoCloseWindow
  2347.  
  2348. #undef dontSaveChanges
  2349.  
  2350. // --------------------------------------------------------------------------------------------------------------
  2351. #pragma segment Main
  2352.  
  2353. static OSErr    DetermineWindowTypeOrOpen(
  2354.     FSSpecPtr theSpec, OSType theType,                 // optional input params -- file to open
  2355.     OSType *returnedTypeList, short * pNumTypes,    // optional input params -- returns list of files
  2356.     Boolean *pWasAlreadyOpen)                        // optional input params -- was file already open
  2357. {
  2358.     OSErr        anErr = noErr;
  2359.     OSType        typeList[20];
  2360.     OSType        docList[20];
  2361.     short        numTypes;
  2362.  
  2363.     // use local copies if the input params are nil    
  2364.     if (returnedTypeList == nil)
  2365.         returnedTypeList = &typeList[0];
  2366.     if (pNumTypes == nil)
  2367.         pNumTypes = &numTypes;
  2368.     *pNumTypes = 0;
  2369.     
  2370.     // Load up all of the file types we know how to handle
  2371.     AboutGetFileTypes(returnedTypeList, docList, pNumTypes);
  2372.     PICTGetFileTypes(returnedTypeList, docList, pNumTypes);
  2373.     MovieGetFileTypes(returnedTypeList, docList, pNumTypes);
  2374.     ClipboardGetFileTypes(returnedTypeList, docList, pNumTypes);
  2375.     TextGetFileTypes(returnedTypeList, docList, pNumTypes);
  2376.     GXGetFileTypes(returnedTypeList, docList, pNumTypes);
  2377.     ThreeDGetFileTypes(returnedTypeList, docList, pNumTypes);
  2378.  
  2379.     if (theSpec != nil)
  2380.         {
  2381.         short         index;
  2382.         OSType        windowType = '????';
  2383.  
  2384.         for (index = 0; index < (*pNumTypes); ++index)
  2385.             if (theType == returnedTypeList[index])
  2386.                 windowType = docList[index];
  2387.                 
  2388.         if (windowType != '????')
  2389.             {
  2390.             
  2391.             if ( (theType == 'TEXT') || (theType == 'sEXT') )
  2392.                 {
  2393.                 FInfo    theInfo;
  2394.                 
  2395.                 FSpGetFInfo(theSpec, &theInfo);
  2396.                 if ((theInfo.fdFlags & kIsStationary) != 0)
  2397.                     theType = 'sEXT';
  2398.                 else
  2399.                     theType = 'TEXT';
  2400.                 }
  2401.                 
  2402.             anErr = MakeNewWindow(windowType, theSpec, theType, pWasAlreadyOpen);
  2403.             }
  2404.         else
  2405.             anErr = eDocumentWrongKind;
  2406.         }
  2407.         
  2408.         
  2409.     return anErr;
  2410.     
  2411. } // DetermineWindowTypeOrOpen
  2412.  
  2413. // --------------------------------------------------------------------------------------------------------------
  2414. #pragma segment Main
  2415.  
  2416. // Handle update/activate events behind Standard File
  2417. static pascal Boolean OpenDialogFilter(DialogRef theDialog, EventRecord *theEvent,
  2418.                                       short *itemHit, void *myDataPtr)
  2419. {
  2420.     #pragma unused(myDataPtr)
  2421.  
  2422.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  2423.     // drastically changing how the system handles the menu bar during our alert)
  2424.     if ( (theEvent->what == updateEvt) && (theEvent->message != (long)theDialog) )
  2425.         HandleEvent(theEvent);
  2426.  
  2427.     if (StdFilterProc(theDialog, theEvent, itemHit))
  2428.         return(true);
  2429.  
  2430.     return(false);
  2431.  
  2432. } // OpenDialogFilter
  2433.  
  2434. #if GENERATINGCFM
  2435.     static RoutineDescriptor gOpenDialogFilterRD = BUILD_ROUTINE_DESCRIPTOR(uppModalFilterYDProcInfo, OpenDialogFilter);
  2436.     static ModalFilterYDUPP gOpenDialogFilter = &gOpenDialogFilterRD;
  2437. #else
  2438.     static ModalFilterYDUPP gOpenDialogFilter = NewModalFilterYDProc(OpenDialogFilter);
  2439. #endif
  2440.  
  2441.  
  2442. static OSErr DoOpenWindow(void)
  2443. {
  2444.     OSErr                anErr = noErr;
  2445.     short                numTypes;
  2446.     OSType                typeList[20];
  2447.     Point                thePoint = { -1, -1 };
  2448.     FSSpec                fileSpec;
  2449.     OSType                fileType;
  2450.     Boolean                fileSelected;
  2451.     
  2452.     DetermineWindowTypeOrOpen(nil, '????', &typeList[0], &numTypes, nil);
  2453.     
  2454.     if (gMachineInfo.haveNavigationServices)
  2455.         {
  2456.         #if qSingleSelectionOnly
  2457.             fileSelected = OpenFileDialog('ttxt', numTypes, typeList, MyEventProc, &fileSpec, &fileType) == noErr;
  2458.         #else
  2459.             // Open as many documents as the user wishes thruogh Appleevents
  2460.             OpenFileDialog('ttxt', numTypes, typeList, MyEventProc, NULL, NULL);
  2461.             fileSelected = false;
  2462.         #endif
  2463.         }
  2464.     else 
  2465.         {
  2466.         StandardFileReply    sfReply;
  2467.  
  2468.         if (gMachineInfo.haveQuickTime)
  2469.             {
  2470.             CustomGetFilePreview(nil, numTypes, typeList, &sfReply, 0, thePoint, nil, gOpenDialogFilter, nil, nil, nil);
  2471.             }
  2472.         else
  2473.             {
  2474.             CustomGetFile(nil, numTypes, typeList, &sfReply, 0, thePoint, nil, gOpenDialogFilter, nil, nil, nil);
  2475.             }
  2476.             
  2477.         fileSelected= sfReply.sfGood;
  2478.         fileSpec    = sfReply.sfFile;
  2479.         fileType    = sfReply.sfType;
  2480.         }
  2481.     
  2482.     if (fileSelected)
  2483.         {
  2484.         SetWatchCursor();
  2485.         
  2486.         anErr = DetermineWindowTypeOrOpen(&fileSpec, fileType, &typeList[0], &numTypes, nil);
  2487.  
  2488.         SetCursor(&qd.arrow);
  2489.         }
  2490.         
  2491.     return anErr;
  2492.     
  2493. } // DoOpenWindow
  2494.  
  2495. // --------------------------------------------------------------------------------------------------------------
  2496. #pragma segment Main
  2497.  
  2498. static OSErr DoUpdateWindow(WindowRef pWindow)
  2499. {
  2500.     OSErr            anErr = noErr;
  2501.     WindowDataPtr    pData = GetWindowInfo(pWindow);
  2502.     GrafPtr            curPort;
  2503.     
  2504.     // only handle updates for windows we know about
  2505.     if (pData)
  2506.         {
  2507.         GetPort(&curPort);
  2508.         SetPort((GrafPtr)GetWindowPort(pWindow));
  2509.         BeginUpdate(pWindow);
  2510.                     
  2511.         if (pData->pUpdateWindow)
  2512.             anErr = (*(pData->pUpdateWindow)) (pWindow, pData);
  2513.     
  2514.         EndUpdate(pWindow);
  2515.         SetPort(curPort);
  2516.         }
  2517.     
  2518.     return anErr;
  2519.     
  2520. } // DoUpdateWindow
  2521.  
  2522. // --------------------------------------------------------------------------------------------------------------
  2523. #pragma segment Main
  2524.  
  2525. OSErr DoScrollContent(WindowRef pWindow, WindowDataPtr pData, short deltaH, short deltaV)
  2526. {
  2527.     OSErr    anErr = noErr;
  2528.     
  2529.     if ((deltaH) || (deltaV))
  2530.         {
  2531.         // if we have a balloon displayed, update before scrolling anything
  2532.         if (HMIsBalloon())
  2533.             DoUpdateWindow(pWindow);
  2534.         
  2535.         if ((pData) && (pData->pScrollContent))
  2536.             anErr = (*(pData->pScrollContent)) (pWindow, pData, deltaH, deltaV);
  2537.             
  2538.         if (anErr == noErr)
  2539.             {
  2540.             RgnHandle    invalidRgn = NewRgn();
  2541.             
  2542.             ScrollRect(&pData->contentRect, deltaH, deltaV, invalidRgn);
  2543.             InvalRgn(invalidRgn);
  2544.             DisposeRgn(invalidRgn);
  2545.     
  2546.             DoUpdateWindow(pWindow);
  2547.             }
  2548.         }
  2549.     
  2550.     return anErr;
  2551.     
  2552. } // DoScrollContent
  2553.  
  2554. // --------------------------------------------------------------------------------------------------------------
  2555. // BEGIN SCROLL ACTION PROCS
  2556. // --------------------------------------------------------------------------------------------------------------
  2557. #pragma segment Main
  2558.  
  2559. void SetControlAndClipAmount(ControlRef control, short * amount)
  2560. {
  2561.     short        value, max;
  2562.     
  2563.     value = GetControlValue(control);    /* get current value */
  2564.     max = GetControlMaximum(control);        /* and maximum value */
  2565.     *amount = value - *amount;
  2566.     if ( *amount < 0 )
  2567.         *amount = 0;
  2568.     else
  2569.         {
  2570.         if ( *amount > max )
  2571.             *amount = max;
  2572.         }
  2573.     SetControlValue(control, *amount);
  2574.     *amount = value - *amount;        /* calculate the real change */
  2575.     
  2576. } // SetControlAndClipAmount
  2577.  
  2578. // --------------------------------------------------------------------------------------------------------------
  2579. static pascal void VActionProc(ControlRef control, short part)
  2580. {
  2581.     if (part != 0)
  2582.         {
  2583.         WindowRef        pWindow = (**control).contrlOwner;
  2584.         WindowDataPtr     pData = GetWindowInfo(pWindow);
  2585.         short            amount = 0;
  2586.         
  2587.         switch (part)
  2588.             {
  2589.             case kControlUpButtonPart:
  2590.                 amount = pData->vScrollAmount;
  2591.                 break;
  2592.                 
  2593.             case kControlDownButtonPart:
  2594.                 amount = -pData->vScrollAmount;
  2595.                 break;
  2596.                 
  2597.             // vertical page scrolling should be a multiple of the incremental scrolling -- so that
  2598.             // we avoid half-lines of text at the bottom of pages.
  2599.             
  2600.             // More generically, if there was a method for dealing with text scrolling by a non-constant
  2601.             // amount, this would be better -- but SimpleText currently doesn't have a framework to allow
  2602.             // the document object to override the scroll amount dynamically.  Maybe something to add in
  2603.             // the future.
  2604.             case kControlPageUpPart:
  2605.                 amount = (((pData->contentRect.bottom - pData->contentRect.top) / pData->vScrollAmount)-1) * pData->vScrollAmount;
  2606.                 if (amount == 0)
  2607.                     amount = pData->contentRect.bottom - pData->contentRect.top;
  2608.                 break;
  2609.  
  2610.             case kControlPageDownPart:
  2611.                 amount = (((pData->contentRect.top - pData->contentRect.bottom) / pData->vScrollAmount)+1) * pData->vScrollAmount;
  2612.                 if (amount == 0)
  2613.                     amount = pData->contentRect.top - pData->contentRect.bottom;
  2614.                 break;
  2615.             }
  2616.         
  2617.         SetControlAndClipAmount(control, &amount);
  2618.         if (amount != 0)
  2619.             DoScrollContent(pWindow, pData, 0, amount);
  2620.         }
  2621.         
  2622. } // VActionProc
  2623.  
  2624. #if GENERATINGCFM
  2625.     static RoutineDescriptor gVActionProcRD = BUILD_ROUTINE_DESCRIPTOR(uppControlActionProcInfo, VActionProc);
  2626.     static ControlActionUPP gVActionProc = &gVActionProcRD;
  2627. #else
  2628.     static ControlActionUPP gVActionProc = VActionProc;
  2629. #endif
  2630.  
  2631. // --------------------------------------------------------------------------------------------------------------
  2632. static pascal void HActionProc(ControlRef control, short part)
  2633. {
  2634.     if (part != 0)
  2635.         {
  2636.         WindowRef        pWindow = (**control).contrlOwner;
  2637.         WindowDataPtr     pData = GetWindowInfo(pWindow);
  2638.         short            amount = 0;
  2639.         
  2640.         switch (part)
  2641.             {
  2642.             case kControlUpButtonPart:
  2643.                 amount = pData->hScrollAmount;
  2644.                 break;
  2645.                 
  2646.             case kControlDownButtonPart:
  2647.                 amount = -pData->hScrollAmount;
  2648.                 break;
  2649.                 
  2650.             case kControlPageUpPart:
  2651.                 amount = pData->contentRect.right - pData->contentRect.left;
  2652.                 break;
  2653.  
  2654.             case kControlPageDownPart:
  2655.                 amount = pData->contentRect.left - pData->contentRect.right;
  2656.                 break;
  2657.             }
  2658.         
  2659.         SetControlAndClipAmount(control, &amount);
  2660.         if (amount != 0)
  2661.             DoScrollContent(pWindow, pData, amount, 0);
  2662.         }
  2663.         
  2664. } // HActionProc
  2665.  
  2666. #if GENERATINGCFM
  2667.     static RoutineDescriptor gHActionProcRD = BUILD_ROUTINE_DESCRIPTOR(uppControlActionProcInfo, HActionProc);
  2668.     static ControlActionUPP gHActionProc = &gHActionProcRD;
  2669. #else
  2670.     static ControlActionUPP gHActionProc = HActionProc;
  2671. #endif
  2672.  
  2673. // --------------------------------------------------------------------------------------------------------------
  2674. // END SCROLL ACTION PROCS
  2675. // --------------------------------------------------------------------------------------------------------------
  2676.  
  2677. #pragma segment Main
  2678.  
  2679. static OSErr DoContentClick(WindowRef pWindow)
  2680. {
  2681.     OSErr            anErr = noErr;
  2682.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2683.     
  2684.     
  2685.     if ( pData )
  2686.         {
  2687.         SetPort((GrafPtr) GetWindowPort(pWindow));
  2688.         
  2689.         if (pData->pContentClick)
  2690.             {
  2691.             // let the object handle the click if it wishes to
  2692.             anErr = (*(pData->pContentClick)) (pWindow, pData, &gEvent);
  2693.             }
  2694.         
  2695.         if (anErr == noErr) 
  2696.             {
  2697.             ControlRef        theControl;
  2698.             short            part;
  2699.             
  2700.             GlobalToLocal(&gEvent.where);
  2701.             part = FindControl(gEvent.where, pWindow, &theControl);
  2702.             switch (part)
  2703.                 {
  2704.                 // do nothing for viewRect case
  2705.                 case 0:
  2706.                     break;
  2707.  
  2708.                 // track the thumb, and then update all at once
  2709.                 case kControlIndicatorPart:
  2710.                     {
  2711.                     short    value = GetControlValue(theControl);
  2712.                     
  2713.                     part = TrackControl(theControl, gEvent.where, nil);
  2714.                     if (part != 0)
  2715.                         {
  2716.                         // turn the value into a delta
  2717.                         value -= GetControlValue(theControl);
  2718.                         
  2719.                         // if we actually moved
  2720.                         if (value != 0)
  2721.                             {
  2722.                             if (theControl == pData->hScroll)
  2723.                                 DoScrollContent(pWindow, pData, value, 0);
  2724.                             if (theControl == pData->vScroll)
  2725.                                 DoScrollContent(pWindow, pData, 0, value);
  2726.                                 
  2727.                             }
  2728.                         }
  2729.                     }
  2730.                     break;
  2731.                     
  2732.                 // track the control, and scroll as we go
  2733.                 default:
  2734.                     if (theControl)
  2735.                         {
  2736.                         if (theControl == pData->hScroll)
  2737.                             part = TrackControl(theControl, gEvent.where, gHActionProc);
  2738.                         if (theControl == pData->vScroll)
  2739.                             part = TrackControl(theControl, gEvent.where, gVActionProc);
  2740.                         }
  2741.                     break;
  2742.                 }
  2743.             }
  2744.  
  2745.         }
  2746.  
  2747.         
  2748.     return anErr;
  2749.     
  2750. } // DoContentClick
  2751.  
  2752. // --------------------------------------------------------------------------------------------------------------
  2753. #pragma segment Main
  2754.  
  2755. static OSErr DoGrowWindow(WindowRef pWindow, EventRecord *pEvent)
  2756. {
  2757.     OSErr            anErr = noErr;
  2758.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2759.     Rect            tempRect;
  2760.     LongRect        docRect;
  2761.     long            growResult;
  2762.     
  2763.     if (pData)
  2764.         {
  2765.         GrafPtr    pPort = (GrafPtr)GetWindowPort(pWindow);
  2766.         
  2767.         SetPort(pPort);
  2768.         
  2769.         RectToLongRect(&pData->contentRect, &docRect);
  2770.         if (pData->pGetDocumentRect)
  2771.             (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, true);
  2772.         if (pData->vScroll)
  2773.             docRect.right += 16;
  2774.         if (pData->hScroll)
  2775.             docRect.bottom += 16;
  2776.         
  2777.         if ( (pData->hasGrow) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  2778.             {
  2779.             docRect.right += 16;
  2780.             docRect.bottom += 16;
  2781.             }
  2782.             
  2783.         // set up resize constraints
  2784.         tempRect.left = pData->minHSize;
  2785.         if (tempRect.left == 0)
  2786.             tempRect.left = kMinDocSize;
  2787.         tempRect.right = docRect.right - docRect.left;
  2788.         if (tempRect.right < tempRect.left)
  2789.             tempRect.right = tempRect.left;
  2790.         tempRect.top = pData->minVSize;
  2791.         if (tempRect.top == 0)
  2792.             tempRect.top = kMinDocSize;
  2793.         tempRect.bottom = docRect.bottom - docRect.top;
  2794.         if (tempRect.bottom < tempRect.top)
  2795.             tempRect.bottom = tempRect.top;
  2796.             
  2797.         growResult = GrowWindow(pWindow, pEvent->where, &tempRect);
  2798.         if ( growResult != 0 ) 
  2799.             {
  2800.             Rect        oldRect;
  2801.             RgnHandle    currentInval = NewRgn();
  2802.             Boolean        needInvalidate;
  2803.             
  2804.             // save old content area
  2805.             oldRect = pData->contentRect;
  2806.             
  2807.             // save old pending update
  2808.             GetWindowUpdateRgn(pWindow, currentInval);
  2809.             OffsetRgn(currentInval, pPort->portBits.bounds.left, pPort->portBits.bounds.top);
  2810.             
  2811.             // grow window and recalc what is needed
  2812.             SizeWindow(pWindow, growResult & 0xFFFF, growResult >> 16, true);
  2813.             AdjustScrollBars(pWindow, true, true, &needInvalidate);
  2814.             
  2815.             if (needInvalidate)
  2816.                 {
  2817.                 InvalRect(&pData->contentRect);
  2818.                 }
  2819.             else
  2820.                 {
  2821.                 // don't bother to redraw things that haven't changed
  2822.                 SectRect(&oldRect, &pData->contentRect, &oldRect);
  2823.                 ValidRect(&oldRect);
  2824.                 
  2825.                 // but, if a pending update was there, be sure to deal with that!
  2826.                 InvalRgn(currentInval);
  2827.                 }
  2828.  
  2829.             // if we have offset scrollbars, then force a redraw of them
  2830.             if (pData->hScrollOffset)
  2831.                 {
  2832.                 oldRect = GetWindowPort(pWindow)->portRect;
  2833.                 oldRect.right = oldRect.left + pData->hScrollOffset;
  2834.                 oldRect.top = oldRect.bottom - kScrollBarSize;
  2835.                 InvalRect(&oldRect);
  2836.                 }
  2837.             if (pData->vScrollOffset)
  2838.                 {
  2839.                 oldRect = GetWindowPort(pWindow)->portRect;
  2840.                 oldRect.bottom = oldRect.top + pData->vScrollOffset;
  2841.                 oldRect.left = oldRect.right - kScrollBarSize;
  2842.                 InvalRect(&oldRect);
  2843.                 }
  2844.  
  2845.             DisposeRgn(currentInval);
  2846.             }
  2847.             
  2848.         }
  2849.         
  2850.     
  2851.     return anErr;
  2852.     
  2853. } // DoGrowWindow
  2854.  
  2855. // --------------------------------------------------------------------------------------------------------------
  2856. #pragma segment Main
  2857.  
  2858. static OSErr DoZoomWindow(WindowRef pWindow, short zoomDir)
  2859. {
  2860.     Rect                *windRect, *zoomRect;
  2861.     Rect                globalPortRect, theSect, dGDRect;
  2862.     GDHandle            nthDevice, dominantGDevice;
  2863.     long                sectArea, greatestArea;
  2864.     short                 hMax, vMax;
  2865.     
  2866.     // determine the max size of the window
  2867.     {
  2868.     WindowDataPtr        pData = GetWindowInfo(pWindow);
  2869.     LongRect            docRect;
  2870.     
  2871.     RectToLongRect(&pData->contentRect, &docRect);
  2872.     if (pData->pGetDocumentRect)
  2873.         (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, true);
  2874.     if (pData->vScroll)
  2875.         docRect.right += kScrollBarSize;
  2876.     if (pData->hScroll)
  2877.         docRect.bottom += kScrollBarSize;
  2878.     
  2879.     if ( (pData->hasGrow) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  2880.         {
  2881.         docRect.right += kScrollBarSize;
  2882.         docRect.bottom += kScrollBarSize;
  2883.         }
  2884.  
  2885.     hMax = docRect.right - docRect.left;
  2886.     vMax = docRect.bottom - docRect.top;
  2887.     }
  2888.     
  2889.     SetPort((GrafPtr) GetWindowPort(pWindow));
  2890.     EraseRect(&GetWindowPort(pWindow)->portRect);    // recommended for cosmetic reasons
  2891.  
  2892.     if (zoomDir == inZoomOut) 
  2893.         {
  2894.  
  2895.         /*
  2896.          *    ZoomWindow() is a good basic tool, but it doesn't do everything necessary to
  2897.          *    implement a good human interface when zooming. In fact it's not even close for
  2898.          *    more high-end hardware configurations. We must help it along by calculating an
  2899.          *    appropriate window size and location any time a window zooms out.
  2900.          */
  2901.         {
  2902.         RgnHandle    structRgn = NewRgn();
  2903.         
  2904.         GetWindowStructureRgn(pWindow, structRgn);
  2905.         windRect = &(**structRgn).rgnBBox;
  2906.         DisposeRgn(structRgn);
  2907.         }
  2908.         dominantGDevice = nil;
  2909.         if (gMachineInfo.theEnvirons.hasColorQD) 
  2910.             {
  2911.  
  2912.             /*
  2913.              *    Color QuickDraw implies the possibility of multiple monitors. This is where
  2914.              *    zooming becomes more interesting. One should zoom onto the monitor containing
  2915.              *    the greatest portion of the window. This requires walking the gDevice list.
  2916.              */
  2917.  
  2918.             nthDevice = GetDeviceList();
  2919.             greatestArea = 0;
  2920.             while (nthDevice != nil) 
  2921.                 {
  2922.                 if (TestDeviceAttribute(nthDevice, screenDevice)) 
  2923.                     {
  2924.                     if (TestDeviceAttribute(nthDevice, screenActive)) 
  2925.                         {
  2926.                         SectRect(windRect, &(**nthDevice).gdRect, &theSect);
  2927.                         sectArea = (long) RectWidth(theSect) * (long) RectHeight(theSect);
  2928.                         if (sectArea > greatestArea) 
  2929.                             {
  2930.                             greatestArea = sectArea;        // save the greatest intersection
  2931.                             dominantGDevice = nthDevice;    // and which device it belongs to
  2932.                             }
  2933.                         }
  2934.                     }
  2935.                 nthDevice = GetNextDevice(nthDevice);
  2936.                 }
  2937.             }
  2938.  
  2939.         /*
  2940.          *    At this point, we know the dimensions of the window we're zooming, and we know
  2941.          *    what screen we're going to put it on. To be more specific, however, we need a
  2942.          *    rectangle which defines the maximum dimensions of the resized window's contents.
  2943.          *    This rectangle accounts for the thickness of the window frame, the menu bar, and
  2944.          *    one or two pixels around the edges for cosmetic compatibility with ZoomWindow().
  2945.          */
  2946.  
  2947.         if (dominantGDevice != nil) 
  2948.             {
  2949.             dGDRect = (**dominantGDevice).gdRect;
  2950.             if (dominantGDevice == GetMainDevice())        // account for menu bar on main device
  2951.                 dGDRect.top += GetMBarHeight();
  2952.             }
  2953.         else 
  2954.             {
  2955.             dGDRect = qd.screenBits.bounds;                // if no gDevice, use default monitor
  2956.             dGDRect.top += GetMBarHeight();
  2957.             }
  2958.  
  2959.         globalPortRect = GetWindowPort(pWindow)->portRect;
  2960.         LocalToGlobal(&TopLeft(globalPortRect));        // calculate the window's portRect
  2961.         LocalToGlobal(&BotRight(globalPortRect));        // in global coordinates
  2962.  
  2963.         // account for the window frame and inset it a few pixels
  2964.         dGDRect.left    += 2 + globalPortRect.left - windRect->left;
  2965.         dGDRect.top        += 2 + globalPortRect.top - windRect->top;
  2966.         dGDRect.right    -= 1 + windRect->right - globalPortRect.right;
  2967.         dGDRect.bottom    -= 1 + windRect->bottom - globalPortRect.bottom;
  2968.  
  2969.         /*
  2970.          *    Now we know exactly what our limits are, and since there are input parameters
  2971.          *    specifying the dimensions we'd like to see, we can move and resize the zoom
  2972.          *    state rectangle for the best possible results. We have three goals in this:
  2973.          *    1. Display the window entirely visible on a single device.
  2974.          *    2. Resize the window to best represent the dimensions of the document itself.
  2975.          *    3. Move the window as short a distance as possible to achieve #1 and #2.
  2976.          */
  2977.  
  2978.         zoomRect = &(**(WStateDataHandle) ((WindowPeek) pWindow)->dataHandle).stdState;
  2979.  
  2980.         /*
  2981.          *    Initially set the zoom rectangle to the size requested by the input parameters,
  2982.          *    although not smaller than a minimum size. We do this without moving the origin.
  2983.          */
  2984.  
  2985.         zoomRect->right = (zoomRect->left = globalPortRect.left) +
  2986.                                 Max(hMax, kMinDocSize);
  2987.         zoomRect->bottom = (zoomRect->top = globalPortRect.top) +
  2988.                                 Max(vMax, kMinDocSize);
  2989.  
  2990.         // Shift the entire rectangle if necessary to bring its origin inside dGDRect.
  2991.         OffsetRect(zoomRect,
  2992.                     Max(dGDRect.left - zoomRect->left, 0),
  2993.                     Max(dGDRect.top - zoomRect->top, 0));
  2994.  
  2995.         /*
  2996.          *    Shift the rectangle up and/or to the left if necessary to accomodate the view,
  2997.          *    and if it is possible to do so. The rectangle may not be moved such that its
  2998.          *    origin would fall outside of dGDRect.
  2999.          */
  3000.  
  3001.         OffsetRect(zoomRect,
  3002.                     -Pin(zoomRect->right - dGDRect.right, 0, zoomRect->left - dGDRect.left),
  3003.                     -Pin(zoomRect->bottom - dGDRect.bottom, 0, zoomRect->top - dGDRect.top));
  3004.  
  3005.         // Clip expansion to dGDRect, in case view is larger than dGDRect.
  3006.         zoomRect->right = Min(zoomRect->right, dGDRect.right);
  3007.         zoomRect->bottom = Min(zoomRect->bottom, dGDRect.bottom);
  3008.         }
  3009.  
  3010.     ZoomWindow(pWindow, zoomDir, pWindow == FrontWindow());
  3011.     
  3012.     AdjustScrollBars(pWindow, true, true, nil);
  3013.  
  3014.     InvalRect(&GetWindowPort(pWindow)->portRect);
  3015.     
  3016.     return noErr;
  3017.     
  3018. } // DoZoomWindow
  3019.  
  3020. // --------------------------------------------------------------------------------------------------------------
  3021. #pragma segment Main
  3022.  
  3023. OSErr DoActivate(WindowRef pWindow, Boolean activating)
  3024. {
  3025.  
  3026.     OSErr            anErr = noErr;
  3027.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  3028.     
  3029.     SetPort((GrafPtr) GetWindowPort(pWindow));
  3030.     
  3031.     if ( pData )
  3032.         {
  3033.         if (pData->pActivateEvent)
  3034.             anErr = (*(pData->pActivateEvent)) (pWindow, pData, activating);
  3035.             
  3036.         if (anErr == noErr)
  3037.             {
  3038.             if (activating)
  3039.                 {
  3040.                 // Reshow all controls on activation
  3041.                 if (pData->hScroll)
  3042.                     ShowControl(pData->hScroll);
  3043.                 if (pData->vScroll)
  3044.                     ShowControl(pData->vScroll);
  3045.                 }
  3046.             else
  3047.                 {
  3048.                 // Hide all controls on deactivation
  3049.                 if (pData->hScroll)
  3050.                     HideControl(pData->hScroll);
  3051.                 if (pData->vScroll)
  3052.                     HideControl(pData->vScroll);
  3053.                 }
  3054.                 
  3055.             if (pData->hasGrow) 
  3056.                 {
  3057.                 Rect    growIconRect;
  3058.                 
  3059.                 CalculateGrowIcon(pData, &growIconRect);
  3060.                 InvalRect(&growIconRect);
  3061.                 }
  3062.             }
  3063.         }
  3064.  
  3065.     AdjustMenus(pWindow, true, false);
  3066.         
  3067.     return anErr;
  3068.     
  3069. } // DoActivate
  3070.  
  3071. // --------------------------------------------------------------------------------------------------------------
  3072. #pragma segment Main
  3073.  
  3074. static OSErr    DoStartupGX(void)
  3075. {
  3076.     gxGraphicsError    anErr = noErr;
  3077.     
  3078.     if (!gMachineInfo.haveStartedGX)
  3079.         {
  3080.         GXEnterGraphics();
  3081.         GXGetGraphicsError(&anErr);
  3082.         if ( (anErr == noErr) && (GXGetGraphicsClient() == nil) )
  3083.             anErr = out_of_memory;
  3084.         if (anErr == noErr)
  3085.             {
  3086.             anErr = GXInitPrinting();
  3087.             if (anErr != noErr)
  3088.                 GXExitGraphics();
  3089.             }
  3090.             
  3091.         if (anErr != noErr)
  3092.             GXSetGraphicsClient(nil);
  3093.         }
  3094.         
  3095.     if (GXGetGraphicsClient() == nil)
  3096.         anErr = out_of_memory;
  3097.  
  3098.     if (anErr == noErr)
  3099.         gMachineInfo.haveStartedGX = true;
  3100.  
  3101.     return anErr;
  3102.     
  3103. } // DoStartupGX
  3104.  
  3105. // --------------------------------------------------------------------------------------------------------------
  3106. #pragma segment Main
  3107.  
  3108. OSErr    DoDefault(WindowDataPtr     pData)
  3109. {
  3110.     OSErr    anErr = noErr;
  3111.     
  3112.     if (gMachineInfo.haveGX)
  3113.         {
  3114.         // start up GX, if needed
  3115.         anErr = DoStartupGX();
  3116.  
  3117.         if (anErr == noErr)
  3118.             {
  3119.             
  3120.             // default the job if we don't have it
  3121.             if (pData->hPrint == nil)
  3122.                 {
  3123.                 anErr = GXNewJob((gxJob*)&pData->hPrint);
  3124.                 if (anErr == noErr)
  3125.                     {
  3126.                     GXInstallApplicationOverride(pData->hPrint, gxPrintingEventMsg,         gPrintingEventMessage);
  3127.                     GXInstallApplicationOverride(pData->hPrint, gxCompleteSpoolFileMsg,     gCompleteSpoolFileMessage);
  3128.                     }
  3129.                 }
  3130.             }
  3131.         }
  3132.     else
  3133.         {
  3134.         PrOpen();
  3135.         anErr = PrError();
  3136.         if (anErr == noErr)
  3137.             {
  3138.             if (pData->hPrint == nil)
  3139.                 {
  3140.                 pData->hPrint = NewHandleClear(sizeof(TPrint));
  3141.                 anErr = MemError();
  3142.                 if (anErr == noErr)
  3143.                     PrintDefault(pData->hPrint);
  3144.                 }
  3145.                 
  3146.             }
  3147.         PrClose();
  3148.         }
  3149.         
  3150.     return anErr;
  3151.     
  3152. } // DoDefault
  3153.  
  3154. // --------------------------------------------------------------------------------------------------------------
  3155. #pragma segment Main
  3156.  
  3157. static void SetupForPrintDialogs(gxEditMenuRecord * pEdit)
  3158. {
  3159.     MenuHandle    menu;
  3160.     short        menuID, itemID;
  3161.  
  3162.     CommandToIDs(cCut,   &pEdit->editMenuID, &pEdit->cutItem);
  3163.     CommandToIDs(cCopy,  &pEdit->editMenuID, &pEdit->copyItem);
  3164.     CommandToIDs(cPaste, &pEdit->editMenuID, &pEdit->pasteItem);
  3165.     CommandToIDs(cClear, &pEdit->editMenuID, &pEdit->clearItem);
  3166.     CommandToIDs(cUndo,  &pEdit->editMenuID, &pEdit->undoItem);
  3167.     
  3168.     // diable everything we don't want to deal with
  3169.     for (menuID = mApple; menuID <= mLastMenu; ++menuID)
  3170.         {
  3171.         menu = GetMenuHandle(menuID);
  3172.         
  3173.         if (menu)
  3174.             {
  3175.             switch (menuID)
  3176.                 {
  3177.                 case mApple:
  3178.                     CommandToIDs(cAbout, &menuID, &itemID);
  3179.                     DisableItem(menu, itemID);
  3180.                     break;
  3181.                     
  3182.                 case mEdit:
  3183.                     CommandToIDs(cSelectAll, &menuID, &itemID);
  3184.                     DisableItem(menu, itemID);
  3185.                     CommandToIDs(cShowClipboard, &menuID, &itemID);
  3186.                     DisableItem(menu, itemID);
  3187.                     break;
  3188.                     
  3189.                 default:
  3190.                     DisableItem(menu, 0);
  3191.                     break;
  3192.                 }
  3193.             }
  3194.         }
  3195.     
  3196.     // Disable the current indicator because the dialogs are moveable modal    
  3197.     HiliteMenu(0);
  3198.         
  3199. } // SetupForPrintDialogs
  3200.  
  3201. // --------------------------------------------------------------------------------------------------------------
  3202. #pragma segment Main
  3203.  
  3204. OSErr    DoPageSetup(WindowRef pWindow)
  3205. {
  3206.     OSErr            anErr = noErr;
  3207.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  3208.         
  3209.     anErr = DoDefault(pData);
  3210.     nrequire(anErr, DoDefault);
  3211.     
  3212.     if (gMachineInfo.haveGX)
  3213.         {
  3214.         gxEditMenuRecord    theEdit;
  3215.         
  3216.         SetupForPrintDialogs(&theEdit);
  3217.         
  3218.         GXJobDefaultFormatDialog(pData->hPrint, &theEdit);
  3219.         anErr = GXGetJobError(pData->hPrint);
  3220.             
  3221.         AdjustMenus(pWindow, true, true);
  3222.         }
  3223.     else
  3224.         {
  3225.         PrOpen();
  3226.         anErr = PrError();
  3227.         if (anErr == noErr)
  3228.             {
  3229.             SetCursor(&qd.arrow);
  3230.             PrStlDialog(pData->hPrint);
  3231.             }
  3232.         PrClose();
  3233.         }
  3234.  
  3235. // FALL THROUGH EXCEPTION HANDLING
  3236. DoDefault:        
  3237.     return anErr;
  3238.     
  3239. } // DoPageSetup
  3240.  
  3241. // --------------------------------------------------------------------------------------------------------------
  3242. #pragma segment Main
  3243.  
  3244. static OSErr    DoPrintSetup(WindowRef pWindow, StringPtr pPrinterName)
  3245. {
  3246.     OSErr            anErr = noErr;
  3247.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  3248.         
  3249.     anErr = DoDefault(pData);
  3250.     nrequire(anErr, DoDefault);
  3251.     
  3252.     if (gMachineInfo.haveGX)
  3253.         {
  3254.         gxEditMenuRecord    theEdit;
  3255.         gxDialogResult        result;
  3256.         
  3257.         // toss any previous errors that might be around
  3258.         (void)GXGetJobError(pData->hPrint);
  3259.         
  3260.         if ( (pPrinterName) && (pPrinterName[0] != 0) )
  3261.             GXSelectJobOutputPrinter(pData->hPrint, pPrinterName);
  3262.         else
  3263.             {
  3264.             SetupForPrintDialogs(&theEdit);
  3265.             result = GXJobPrintDialog(pData->hPrint, &theEdit);
  3266.             AdjustMenus(pWindow, true, true);
  3267.             }
  3268.         if (anErr == noErr)
  3269.             {
  3270.             anErr = GXGetJobError(pData->hPrint);
  3271.             if ( (anErr == noErr) && (result == gxCancelSelected) )
  3272.                 anErr = eUserCanceled;
  3273.             }
  3274.         }
  3275.     else
  3276.         {
  3277.         PrOpen();
  3278.         anErr = PrError();
  3279.         if (anErr == noErr)
  3280.             {
  3281.             SetCursor(&qd.arrow);
  3282.             if (PrJobDialog(pData->hPrint) == false)
  3283.                 anErr = eUserCanceled;
  3284.             }
  3285.             
  3286.         PrClose();
  3287.         }
  3288.         
  3289. // FALL THROUGH EXCEPTION HANDLING
  3290. DoDefault:        
  3291.     return anErr;
  3292.     
  3293. } // DoPrintSetup
  3294.  
  3295. // --------------------------------------------------------------------------------------------------------------
  3296. #pragma segment Main
  3297.  
  3298. static OSErr    DoPrint(WindowRef pWindow, void * hPrint, Boolean oneCopy)
  3299. {
  3300.     gxGraphicsError        anErr = noErr;
  3301.     WindowDataPtr         pData = GetWindowInfo(pWindow);
  3302.     Boolean                didAllocate = false;
  3303.     
  3304.     // use a watch cursor while printing
  3305.     SetWatchCursor();
  3306.     
  3307.     if (gMachineInfo.haveGX)
  3308.         {
  3309.         // startup GX, if needed
  3310.         anErr = DoStartupGX();
  3311.  
  3312.         if ( (anErr == noErr) && (hPrint == nil) )
  3313.             {
  3314.             anErr = GXNewJob((gxJob*)&hPrint);
  3315.             if (anErr == noErr)            
  3316.                 {
  3317.                 GXInstallApplicationOverride(hPrint, gxPrintingEventMsg,         gPrintingEventMessage);
  3318.                 GXInstallApplicationOverride(hPrint, gxCompleteSpoolFileMsg,     gCompleteSpoolFileMessage);
  3319.                 didAllocate = true;
  3320.                 }
  3321.             }
  3322.  
  3323.         if (anErr == noErr)
  3324.             {            
  3325.             Str255    docName;
  3326.             
  3327.             if (oneCopy)
  3328.                 {
  3329.                 gxCopiesInfo theCopies;
  3330.                 
  3331.                 theCopies.copies = 1;
  3332.                 AddCollectionItem(GXGetJobCollection(hPrint), gxCopiesTag, gxPrintingTagID, sizeof(theCopies), &theCopies);
  3333.                 }
  3334.                 
  3335.             GXSetJobRefCon(hPrint, pData);
  3336.             
  3337.             GetWTitle(pWindow, docName);
  3338.             GXStartJob(hPrint, docName, 0);
  3339.             anErr = GXGetJobError(hPrint);
  3340.             if (anErr == noErr)
  3341.                 {
  3342.                 long        first, last, pageIndex;
  3343.                 Rect        pageRect;
  3344.                 CGrafPort    thePort;
  3345.                 
  3346.                 // determine size of page, and number of pages
  3347.                 {
  3348.                 gxRectangle    pageSize;
  3349.                 GXGetFormatDimensions(GXGetJobFormat(hPrint, 1), &pageSize, nil);
  3350.                 pageRect.top    = pageSize.top >> 16;
  3351.                 pageRect.left    = pageSize.left >> 16;
  3352.                 pageRect.bottom    = pageSize.bottom >> 16;
  3353.                 pageRect.right    = pageSize.right >> 16;
  3354.                 }            
  3355.                 GXGetJobPageRange(hPrint, &first, &last);
  3356.                 anErr = GXGetJobError(hPrint);
  3357.                 if (first < 1)
  3358.                     first = 1;
  3359.                 if (last < first)
  3360.                     last = first;
  3361.                     
  3362.                 if (anErr == noErr)
  3363.                     {
  3364.                     // make a port to perform translation in
  3365.                     OpenCPort(&thePort);
  3366.                     
  3367.                     for (pageIndex = first; pageIndex <= last; ++pageIndex)
  3368.                         {
  3369.                         SetPort((GrafPtr) &thePort);
  3370.                         
  3371.                         if (pData->documentOutputsGX)
  3372.                             anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &pageIndex);
  3373.                         else
  3374.                             {
  3375.                             Point                    patStretch = {1,1};
  3376.                             gxTranslationOption     options = gxOptimizedTranslation;
  3377.                             CatchRefCon                theRefCon;
  3378.                             
  3379.                             if (GXGetPrinterDriverType(GXGetJobPrinter(hPrint)) == 'post')
  3380.                                 options += gxPostScriptTargetTranslation;
  3381.                             else
  3382.                                 options += gxRasterTargetTranslation;
  3383.                             
  3384.                             theRefCon.theJob                    = hPrint;
  3385.                             theRefCon.doLayout                    = (pData->originalFileType == 'TEXT');
  3386.                             theRefCon.thePage                     = GXNewShape(gxPictureType);
  3387.                             theRefCon.thePageRectangle.top         = ff(pageRect.top);
  3388.                             theRefCon.thePageRectangle.left     = ff(pageRect.left);
  3389.                             theRefCon.thePageRectangle.bottom     = ff(pageRect.bottom);
  3390.                             theRefCon.thePageRectangle.right     = ff(pageRect.right);
  3391.     
  3392.                             GXInstallQDTranslator(
  3393.                                         qd.thePort,
  3394.                                         options,
  3395.                                         &pageRect,
  3396.                                         &pageRect,
  3397.                                         patStretch,
  3398.                                         gSimpleCatchShape,
  3399.                                         &theRefCon);
  3400.                             GXGetGraphicsError(&anErr);
  3401.                             
  3402.                             if (anErr == noErr)
  3403.                                 {
  3404.                                 long    whichPage = pageIndex;
  3405.                                 
  3406.                                 anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &whichPage);
  3407.                                 GXRemoveQDTranslator(qd.thePort, nil);
  3408.                                 
  3409.                                 GXPrintPage(hPrint, pageIndex, nil, theRefCon.thePage);
  3410.                                 anErr = GXGetJobError(hPrint);
  3411.                                 pageIndex = whichPage;
  3412.                                 }
  3413.                             
  3414.                             GXDisposeShape(theRefCon.thePage);
  3415.                             }
  3416.                         
  3417.                         if (anErr == noErr)
  3418.                             GXGetGraphicsError(&anErr);
  3419.                         
  3420.                         // bail when we are told to stop
  3421.                         if ( (pageIndex == -1) || (anErr != noErr) )
  3422.                             break;
  3423.                         }
  3424.                     
  3425.                     // all done with our temp port and job
  3426.                     CloseCPort(&thePort);
  3427.                     }
  3428.                     
  3429.                 GXFinishJob(hPrint);
  3430.                 if (anErr == noErr) anErr = GXGetJobError(hPrint);
  3431.                 }
  3432.                 
  3433.             }
  3434.                 
  3435.         if (didAllocate)
  3436.             GXDisposeJob(hPrint);
  3437.  
  3438.         // restore those menus!
  3439.         AdjustMenus(pWindow, true, true);
  3440.  
  3441.         }
  3442.     else
  3443.         {
  3444.         TPPrPort    printingPort;
  3445.         
  3446.         PrOpen();
  3447.         anErr = PrError();
  3448.         if (anErr == noErr)
  3449.             {
  3450.             if (hPrint == nil)
  3451.                 {
  3452.                 hPrint = NewHandleClear(sizeof(TPrint));
  3453.                 anErr = MemError();
  3454.                 if (anErr == noErr)
  3455.                     {
  3456.                     PrintDefault(hPrint);
  3457.                     didAllocate = true;
  3458.                     }
  3459.                 }
  3460.             
  3461.             if (anErr == noErr)
  3462.                 {
  3463.                 short    firstPage, lastPage;
  3464.                 
  3465.                 // be sure to get the page range BEFORE calling PrValidate(), 
  3466.                 // which blows it away for many drivers.
  3467.                 firstPage = (**(THPrint)hPrint).prJob.iFstPage;
  3468.                 lastPage = (**(THPrint)hPrint).prJob.iLstPage;
  3469.                 
  3470.                 // make sure the print record is cool to use
  3471.                 PrValidate(hPrint);
  3472.  
  3473.                 // then clear out the job itself -- some drivers don't
  3474.                 // do this from within PrValidate().  We want the job
  3475.                 // clear in case the driver bases background behavior
  3476.                 // from this range (and many do).
  3477.                 (**(THPrint)hPrint).prJob.iFstPage = 1;
  3478.                 (**(THPrint)hPrint).prJob.iLstPage = 9999;
  3479.                 
  3480.                 if (oneCopy)
  3481.                     (** ((THPrint)hPrint)).prJob.iCopies = 1;
  3482.                     
  3483.                 // start printing, and then loop over the pages
  3484.                 printingPort = PrOpenDoc(hPrint, nil, nil);
  3485.                 anErr = PrError();
  3486.                 if (anErr == noErr)
  3487.                     {
  3488.                     long    pageIndex;
  3489.                     Rect    pageRect;
  3490.                     
  3491.                     SetPort((GrafPtr) printingPort);
  3492.                     
  3493.                     pageRect = (**(THPrint)hPrint).prInfo.rPage;
  3494.                     if (firstPage < 1)
  3495.                         firstPage = 1;
  3496.                     if (lastPage < firstPage)
  3497.                         lastPage = firstPage;
  3498.                     for (pageIndex = firstPage; pageIndex <= lastPage; ++pageIndex)
  3499.                         {
  3500.                         PrOpenPage(printingPort, nil);
  3501.                         anErr = PrError();
  3502.                         if (anErr == noErr)
  3503.                             anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &pageIndex);
  3504.                             
  3505.                         PrClosePage(printingPort);
  3506.                         if (anErr == noErr)
  3507.                             anErr = PrError();
  3508.                         if ( (anErr != noErr) || (pageIndex == -1) )
  3509.                             break;
  3510.                         }
  3511.                     }
  3512.                     
  3513.                 // Finish up printing of the document
  3514.                 PrCloseDoc(printingPort);
  3515.                 if (anErr == noErr)
  3516.                     anErr = PrError();
  3517.                 if ( (anErr == noErr) && ((**(THPrint)hPrint).prJob.bJDocLoop == bSpoolLoop) )
  3518.                     {
  3519.                     TPrStatus    theStatus;
  3520.                     
  3521.                     PrPicFile(hPrint, nil, nil, nil, &theStatus);
  3522.                     anErr = PrError();
  3523.                     }
  3524.                 }
  3525.                 
  3526.             if (didAllocate)
  3527.                 DisposeHandle((Handle) hPrint);
  3528.                 
  3529.             }
  3530.         PrClose();
  3531.         }
  3532.     
  3533.         
  3534.     // restore cursor
  3535.     SetCursor(&qd.arrow);
  3536.  
  3537.     return anErr;
  3538.     
  3539. } // DoPrint
  3540.  
  3541. // --------------------------------------------------------------------------------------------------------------
  3542. #pragma segment Main
  3543.  
  3544. OSErr    DoCommand(WindowRef pWindow, short commandID, long menuResult)
  3545. {
  3546.     OSErr            anErr = noErr;
  3547.     WindowDataPtr     pData = nil;
  3548.     
  3549.     if (pWindow)
  3550.         {
  3551.         pData = (WindowDataPtr) GetWindowInfo(pWindow);
  3552.         
  3553.         if ( (pData) && (pData->pCommand) )
  3554.             anErr = (*(pData->pCommand)) (pWindow, pData, commandID, menuResult);
  3555.         }
  3556.     
  3557.     if (anErr == noErr)
  3558.         {
  3559.         // default command handling
  3560.         switch (commandID)
  3561.             {
  3562.             // About box command
  3563.             case cAbout:
  3564.                 if (!BringToFrontIfExists(kAboutWindow))
  3565.                     anErr = MakeNewWindow(kAboutWindow, nil, '????', nil);
  3566.                 break;
  3567.                 
  3568.             case cDeskAccessory:
  3569.                 {
  3570.                 Str255    tempString;
  3571.                 
  3572.                 GetMenuItemText(GetMenuHandle(menuResult>>16), menuResult & 0xFFFF, tempString);
  3573.                 OpenDeskAcc(tempString);
  3574.                 }
  3575.                 break;
  3576.                 
  3577.             // New window command
  3578.             case cNew:
  3579.                 anErr = MakeNewWindow(kTextWindow, nil, 'TEXT', nil);
  3580.                 break;
  3581.                 
  3582.             // Open window command
  3583.             case cOpen:
  3584.                 anErr = DoOpenWindow();
  3585.                 break;
  3586.                 
  3587.             // Close window command
  3588.             case cClose:
  3589.                 anErr = DoCloseWindow(pWindow, false);
  3590.                 break;
  3591.                 
  3592.             case cPageSetup:
  3593.                 anErr = DoPageSetup(pWindow);
  3594.                 break;
  3595.                 
  3596.             case cPrint:
  3597.                 anErr = DoPrintSetup(pWindow, nil);
  3598.                 if (anErr == noErr)
  3599.                     anErr = DoPrint(pWindow, pData->hPrint, false);
  3600.                 break;
  3601.                 
  3602.             case cPrintOneCopy:
  3603.                 anErr = DoPrint(pWindow, pData->hPrint, true);
  3604.                 break;
  3605.                 
  3606.             // get out of here command!
  3607.             case cQuit:
  3608.                 gAllDone = true;
  3609.                 break;
  3610.     
  3611.             // show/hide clipboard
  3612.             case cShowClipboard:
  3613.                 if (!BringToFrontIfExists(kClipboardWindow))
  3614.                     {
  3615.                     anErr = MakeNewWindow(kClipboardWindow, nil, '????', nil);
  3616.                     }
  3617.                 else
  3618.                     {
  3619.                     pWindow = FrontWindow();
  3620.                     anErr = DoCloseWindow(pWindow, false);
  3621.                     }
  3622.                 break;
  3623.                 
  3624.             case cNextPage:
  3625.                 gEvent.what = keyDown;
  3626.                 gEvent.message = kPageDown << 8;
  3627.                 gEvent.modifiers = 0;
  3628.                 DoKeyEvent(pWindow, &gEvent, false);
  3629.                 break;
  3630.                 
  3631.             case cPreviousPage:
  3632.                 gEvent.what = keyDown;
  3633.                 gEvent.message = kPageUp << 8;
  3634.                 gEvent.modifiers = 0;
  3635.                 DoKeyEvent(pWindow, &gEvent, false);
  3636.                 break;
  3637.                 
  3638.             // Do nothing command
  3639.             case cNull:
  3640.                 break;
  3641.                             
  3642.             default:
  3643.                 break;
  3644.             }
  3645.         }
  3646.         
  3647.     // don't report cancels
  3648.     if ( (anErr == iPrAbort) || (anErr == gxPrUserAbortErr) )
  3649.         anErr = noErr;
  3650.     
  3651.     // some errors map to other errors because they are basically the same
  3652.     // This way we can use the same string.
  3653.     if (anErr == out_of_memory)
  3654.         anErr = memFullErr;
  3655.         
  3656.     if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) && (anErr != eUserCanceled) )
  3657.         {
  3658.         // some commands are so similar to other commands that we map their IDs
  3659.         // for the purposes of the error strings
  3660.         if (commandID == cSaveAs)
  3661.             commandID = cSave;
  3662.         if (commandID == cPrintOneCopy)
  3663.             commandID = cPrint;
  3664.             
  3665.         ConductErrorDialog(anErr, commandID, cancel);
  3666.         }
  3667.         
  3668.     // in any case, unhilite the menu selected after command processing is done    
  3669.     HiliteMenu(0);
  3670.     
  3671.     return anErr;
  3672.     
  3673. } // DoCommand
  3674.  
  3675. // --------------------------------------------------------------------------------------------------------------
  3676. #pragma segment Main
  3677.  
  3678. static OSErr    DoMenuCommand(WindowRef pWindow, long menuResult)
  3679. {
  3680.     OSErr    anErr = noErr;
  3681.     short    commandID = cNull;
  3682.     short    ** commandHandle;
  3683.     short    menuID = menuResult >> 16;
  3684.     
  3685.     if (menuID == kHMHelpMenuID)
  3686.         {
  3687.         // close existing database (if any)
  3688.         if ((gAGRefNum != -1) && AGIsDatabaseOpen(gAGRefNum))
  3689.             {
  3690.             AGClose(&gAGRefNum);
  3691.             gAGRefNum = -1;
  3692.             }
  3693.             
  3694.         // and open the database we have found in the past
  3695.         AGOpen(&gAGSpec, 0, nil, &gAGRefNum);
  3696.         }
  3697.     else
  3698.         {
  3699.         if (menuID >= mFontSubMenusStart)
  3700.             {
  3701.             commandID = cSelectFontStyle;
  3702.             }
  3703.         else
  3704.             {
  3705.             // read in the resource that controls this menu
  3706.                 {
  3707.                 short    oldResFile = CurResFile();
  3708.                 
  3709.                 UseResFile(gApplicationResFile);
  3710.                 commandHandle = (short**) Get1Resource('MCMD', menuID);
  3711.                 UseResFile(oldResFile);
  3712.                 anErr = ResError();
  3713.                 nrequire(anErr, FailedToLoadCommandTable);
  3714.                 }
  3715.             
  3716.             if (commandHandle)
  3717.                 {
  3718.                 short    item = menuResult & 0xFFFF;
  3719.                 short    * pCommands = *commandHandle;
  3720.                 
  3721.                 if (item <= pCommands[0])
  3722.                     commandID = pCommands[item];
  3723.                 else
  3724.                     commandID = pCommands[pCommands[0]];
  3725.                 }
  3726.             }
  3727.         
  3728.         anErr = DoCommand(pWindow, commandID, menuResult);
  3729.         }
  3730.     
  3731. // FALL THROUGH EXCEPTION HANDLING
  3732. FailedToLoadCommandTable:
  3733.  
  3734.     return anErr;
  3735.     
  3736. } // DoMenuCommand
  3737.  
  3738.  
  3739. // --------------------------------------------------------------------------------------------------------------
  3740. #pragma segment Main
  3741.  
  3742. static void DoKeyPageDown(WindowRef pWindow, WindowDataPtr pData, Boolean processPageControls)
  3743. {
  3744.  
  3745.     if (GetControlValue(pData->vScroll) < GetControlMaximum(pData->vScroll))
  3746.         VActionProc(pData->vScroll, kControlPageDownPart);
  3747.     else
  3748.         {
  3749.         if ( (processPageControls) && (IsCommandEnabled(cNextPage)) )
  3750.             {
  3751.             short amount;
  3752.  
  3753.             if (DoCommand(pWindow, cNextPage, 0) == eActionAlreadyHandled)
  3754.                 {
  3755.                 amount = GetControlValue(pData->vScroll);
  3756.                 SetControlAndClipAmount(pData->vScroll, &amount);
  3757.                 if (amount != 0)
  3758.                     DoScrollContent(pWindow, pData, 0, amount);
  3759.                 }
  3760.             
  3761.             AdjustMenus(pWindow, true, false);
  3762.             }
  3763.         }
  3764.     
  3765. } // DoKeyPageDown
  3766.  
  3767. // --------------------------------------------------------------------------------------------------------------
  3768. #pragma segment Main
  3769.  
  3770. static void DoKeyPageUp(WindowRef pWindow, WindowDataPtr pData, Boolean processPageControls)
  3771. {
  3772.     if (GetControlValue(pData->vScroll) > GetControlMinimum(pData->vScroll))
  3773.         VActionProc(pData->vScroll, kControlPageUpPart);
  3774.     else
  3775.         {
  3776.         if ( (processPageControls) && (IsCommandEnabled(cPreviousPage)) )
  3777.             {
  3778.             short amount;
  3779.             
  3780.             if (DoCommand(pWindow, cPreviousPage, 0) == eActionAlreadyHandled)
  3781.                 {
  3782.                 amount = -(GetControlMaximum(pData->vScroll)-GetControlValue(pData->vScroll));
  3783.                 SetControlAndClipAmount(pData->vScroll, &amount);
  3784.                 if (amount != 0)
  3785.                     DoScrollContent(pWindow, pData, 0, amount);
  3786.                 }
  3787.             
  3788.             AdjustMenus(pWindow, true, false);
  3789.             }
  3790.         }
  3791.         
  3792. } // DoKeyPageUp
  3793.  
  3794. // --------------------------------------------------------------------------------------------------------------
  3795. #pragma segment Main
  3796.  
  3797. OSErr    DoKeyEvent(WindowRef pWindow, EventRecord * pEvent, Boolean processPageControls)
  3798. {
  3799.     OSErr            anErr = noErr;
  3800.     WindowDataPtr     pData = nil;
  3801.     Boolean            passToObject = false;
  3802.     Boolean         isMotionKey = false;
  3803.     long            menuResult = 0;
  3804.     
  3805.     char keyCode = (pEvent->message >> 8) & charCodeMask;
  3806.  
  3807.     if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3808.         {
  3809.         AdjustMenus(pWindow, true, false);
  3810.         menuResult = MenuKey(pEvent->message & charCodeMask);
  3811.         DoMenuCommand(pWindow, menuResult);
  3812.         pWindow = FrontWindow();
  3813.         }
  3814.  
  3815.     if (menuResult == 0)
  3816.         {
  3817.         if (pWindow)
  3818.             {
  3819.             pData = (WindowDataPtr)GetWindowInfo(pWindow);
  3820.             if ( (pData) && (pData->pKeyEvent) )
  3821.                 passToObject = true;
  3822.             SetPort((GrafPtr) GetWindowPort(pWindow));
  3823.             }
  3824.             
  3825.         if (pData)
  3826.             {
  3827.             switch (keyCode)
  3828.                 {
  3829.                 case kHome: // top of file
  3830.                     isMotionKey = true;
  3831.                     if (pData->vScroll)
  3832.                         {
  3833.                         short amount;
  3834.                         
  3835.                         if ( (processPageControls) && (IsCommandEnabled(cGotoPage)) )
  3836.                             DoCommand(pWindow, cGotoPage, cGotoFirst);
  3837.  
  3838.                         amount = GetControlValue(pData->vScroll);
  3839.                         SetControlAndClipAmount(pData->vScroll, &amount);
  3840.                         if (amount != 0)
  3841.                             DoScrollContent(pWindow, pData, 0, amount);
  3842.                         passToObject = false;
  3843.                         }
  3844.                     break;
  3845.                     
  3846.                 case kEnd: // end of file
  3847.                     isMotionKey = true;
  3848.                     if (pData->vScroll)
  3849.                         {
  3850.                         short amount;
  3851.  
  3852.                         if ( (processPageControls) && (IsCommandEnabled(cGotoPage)) )
  3853.                             DoCommand(pWindow, cGotoPage, cGotoLast);
  3854.                             
  3855.                         amount = -(GetControlMaximum(pData->vScroll)-GetControlValue(pData->vScroll));
  3856.                         SetControlAndClipAmount(pData->vScroll, &amount);
  3857.                         if (amount != 0)
  3858.                             DoScrollContent(pWindow, pData, 0, amount);
  3859.                         passToObject = false;
  3860.                         }
  3861.                     break;
  3862.                     
  3863.                 case kPageUp: // scroll bar page up
  3864.                     isMotionKey = true;
  3865.                     if (pData->vScroll)
  3866.                         {
  3867.                         DoKeyPageUp(pWindow, pData, processPageControls);
  3868.                         passToObject = false;
  3869.                         }
  3870.                     break;
  3871.                     
  3872.                 case kPageDown: // scroll bar page down
  3873.                     isMotionKey = true;
  3874.                     if (pData->vScroll)
  3875.                         {
  3876.                         DoKeyPageDown(pWindow, pData, processPageControls);
  3877.                         passToObject = false;
  3878.                         }
  3879.                     break;
  3880.                             
  3881.                 case kUpArrow:        // scroll bar up arrow
  3882.                     isMotionKey = true;
  3883.                     if ( (pData->vScroll) && (!pData->pKeyEvent) )
  3884.                         {
  3885.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3886.                             DoKeyPageUp(pWindow, pData, processPageControls);
  3887.                         else
  3888.                             VActionProc(pData->vScroll, kControlUpButtonPart);
  3889.                         passToObject = false;
  3890.                         }
  3891.                     break;
  3892.                     
  3893.                 case kDownArrow:    // scroll bar down arrow
  3894.                     isMotionKey = true;
  3895.                     if ( (pData->vScroll) && (!pData->pKeyEvent) )
  3896.                         {
  3897.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3898.                             DoKeyPageDown(pWindow, pData, processPageControls);
  3899.                         else
  3900.                             VActionProc(pData->vScroll, kControlDownButtonPart);
  3901.                         passToObject = false;
  3902.                         }
  3903.                     break;
  3904.         
  3905.                 case kLeftArrow:    // scroll bar left arrow
  3906.                     isMotionKey = true;
  3907.                     if ( (pData->hScroll) && (!pData->pKeyEvent) )
  3908.                         {
  3909.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3910.                             HActionProc(pData->hScroll, kControlPageUpPart);
  3911.                         else
  3912.                             HActionProc(pData->hScroll, kControlUpButtonPart);
  3913.                         passToObject = false;
  3914.                         }
  3915.                     break;
  3916.                     
  3917.                 case kRightArrow:    // scroll bar right arrow
  3918.                     isMotionKey = true;
  3919.                     if ( (pData->hScroll) && (!pData->pKeyEvent) )
  3920.                         {
  3921.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3922.                             HActionProc(pData->hScroll, kControlPageDownPart);
  3923.                         else
  3924.                             HActionProc(pData->hScroll, kControlDownButtonPart);
  3925.                         passToObject = false;
  3926.                         }
  3927.                     break;
  3928.                     }
  3929.         
  3930.             if (passToObject)
  3931.                 anErr = (*(pData->pKeyEvent)) (pWindow, pData, pEvent, isMotionKey);
  3932.             else
  3933.                 {
  3934.                 if ( (pData->documentAcceptsText == false) && !( pEvent->modifiers & cmdKey ) && !(isMotionKey) )
  3935.                     anErr = eDocumentNotModifiable;
  3936.                 }
  3937.             }
  3938.  
  3939.         if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) )
  3940.             ConductErrorDialog(anErr, cTypingCommand, ok);
  3941.             
  3942.         } // (menuResult == 0)
  3943.     
  3944.         
  3945.     return anErr;
  3946.     
  3947. } // DoKeyEvent
  3948.  
  3949. // --------------------------------------------------------------------------------------------------------------
  3950. #pragma segment Main
  3951.  
  3952. static OSErr DoAdjustCursor(WindowRef pWindow)
  3953. {
  3954.     OSErr        anErr = noErr;
  3955.     Point        mouse;
  3956.     Boolean        didAdjust = false;
  3957.     
  3958.     if (pWindow)
  3959.         {
  3960.         // not one of our windows?  don't do anything
  3961.         if (GetWindowKind(pWindow) != userKind)
  3962.             didAdjust = true;
  3963.             
  3964.         SetPort((GrafPtr) GetWindowPort(pWindow));
  3965.         
  3966.         if ( (!didAdjust) && (gMachineInfo.haveTSM) )
  3967.             {
  3968.             GetMouse(&mouse);
  3969.             LocalToGlobal(&mouse);
  3970.             if (SetTSMCursor(mouse))
  3971.                 didAdjust = true;
  3972.             }
  3973.             
  3974.         if (!didAdjust)
  3975.             {
  3976.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  3977.             RgnHandle        content = NewRgn();
  3978.             Point            globalMouse;
  3979.             
  3980.             GetMouse(&mouse);
  3981.             globalMouse = mouse;
  3982.             LocalToGlobal(&globalMouse);
  3983.             
  3984.             GetWindowContentRgn(pWindow, content);
  3985.             if ((pData) && (PtInRgn(globalMouse, content)) && (PtInRect(mouse, &pData->contentRect)))
  3986.                 {
  3987.                 Rect            tempRect;
  3988.                 
  3989.                 tempRect = pData->contentRect;
  3990.                 LocalToGlobal(&TopLeft(tempRect));
  3991.                 LocalToGlobal(&BotRight(tempRect));
  3992.                 
  3993.                 if (pData->pAdjustCursor)
  3994.                     anErr = (*(pData->pAdjustCursor)) (pWindow, pData, &mouse, &tempRect);
  3995.     
  3996.                 RectRgn(gCursorRgn, &tempRect);
  3997.                 }
  3998.             DisposeRgn(content);
  3999.             }
  4000.         else
  4001.             anErr = eActionAlreadyHandled;
  4002.         }
  4003.     
  4004.     // nobody set the cursor, we do it ourselves
  4005.     if (anErr != eActionAlreadyHandled)
  4006.         SetCursor(&qd.arrow);
  4007.         
  4008.     return anErr;
  4009.     
  4010. } // DoAdjustCursor
  4011.  
  4012. // --------------------------------------------------------------------------------------------------------------
  4013. #pragma segment Main
  4014.  
  4015. static long DetermineWaitTime(WindowRef pWindow)
  4016. {
  4017.     long    waitTime = kMaxWaitTime;
  4018.     
  4019.     while (pWindow)
  4020.         {
  4021.         long            newWaitTime;
  4022.         WindowDataPtr    pData = GetWindowInfo(pWindow);
  4023.         
  4024.         if ((pData) && (pData->pCalculateIdleTime))
  4025.             newWaitTime = (*(pData->pCalculateIdleTime)) (pWindow, pData);
  4026.         else
  4027.             newWaitTime = kMaxWaitTime;
  4028.         
  4029.         if (newWaitTime < waitTime)
  4030.             waitTime = newWaitTime;
  4031.             
  4032.         pWindow = GetNextWindow(pWindow);
  4033.         }
  4034.     
  4035.     return(waitTime);
  4036.     
  4037. } // DetermineWaitTime
  4038.  
  4039. // --------------------------------------------------------------------------------------------------------------
  4040. #pragma segment Main
  4041.  
  4042. void HandleEvent(EventRecord * pEvent)
  4043. {
  4044.     WindowRef pWindow = FrontWindow();
  4045.     
  4046.     switch (pEvent->what)
  4047.         {
  4048.         case kHighLevelEvent:
  4049.             AEProcessAppleEvent(pEvent);
  4050.             break;
  4051.             
  4052.         case osEvt:
  4053.             switch ((pEvent->message >> 24) & 0xFF) /* high byte of message */
  4054.                 {        
  4055.                 case mouseMovedMessage:
  4056.                     DoAdjustCursor(pWindow);
  4057.                     break;
  4058.                     
  4059.                 case suspendResumeMessage:        /* suspend/resume is also an activate/deactivate */
  4060.                     gMachineInfo.amInBackground = (pEvent->message & 1) == 0;
  4061.                     if (pWindow)
  4062.                         DoActivate(pWindow, !gMachineInfo.amInBackground);
  4063.                         
  4064.                     // on resume, we must call GXUpdateJob for all active jobs in
  4065.                     // order to get instant activation of extensions and to properly
  4066.                     // handle potential shifting of driver names
  4067.                     if ( (gMachineInfo.haveGX) && (!gMachineInfo.amInBackground) )
  4068.                         {
  4069.                         WindowRef    walkWindows = pWindow;
  4070.                         
  4071.                         while (walkWindows)
  4072.                             {
  4073.                             WindowDataPtr    pData = GetWindowInfo(walkWindows);
  4074.                             if (pData)
  4075.                                 {
  4076.                                 gxJob    theJob = pData->hPrint;
  4077.                                 if (theJob)
  4078.                                     {
  4079.                                     GXUpdateJob(theJob);
  4080.                                     }
  4081.                                 }
  4082.                             walkWindows = GetNextWindow(walkWindows);
  4083.                             }
  4084.                         }
  4085.                     break;
  4086.                 }
  4087.             break;
  4088.             
  4089.         case activateEvt:
  4090.             pWindow = (WindowRef) pEvent->message;
  4091.             DoActivate(pWindow, (pEvent->modifiers & activeFlag) != 0);
  4092.             break;
  4093.                             
  4094.         // disk inserted events must be handled, or uninitialized floppies 
  4095.         // won't be recognized.
  4096.         case diskEvt:
  4097.             if ( HiWord(pEvent->message) != noErr ) 
  4098.                 {
  4099.                 Point    where;
  4100.             
  4101.                 SetPt(&where, 70, 50);
  4102.                 ShowCursor();
  4103.                 (void) DIBadMount(where, pEvent->message);
  4104.                 }        
  4105.             break;
  4106.                 
  4107.         case mouseUp:
  4108.             break;
  4109.             
  4110.         case mouseDown:
  4111.             {
  4112.             short part = FindWindow(pEvent->where, &pWindow);                    
  4113.             
  4114.             switch ( part ) 
  4115.                 {
  4116.                 case inContent:
  4117.                     if (pWindow != FrontWindow())
  4118.                         SelectWindow(pWindow);
  4119.                     else
  4120.                         DoContentClick(pWindow);
  4121.                     break;
  4122.                     
  4123.                 case inGoAway:
  4124.                     if (TrackGoAway(pWindow, pEvent->where) )
  4125.                         DoCommand(pWindow, cClose, 0);
  4126.                     break;
  4127.                     
  4128.                 case inGrow:
  4129.                     DoGrowWindow(pWindow, pEvent);
  4130.                     break;
  4131.                     
  4132.                 case inZoomIn:
  4133.                 case inZoomOut:
  4134.                     if ( TrackBox(pWindow, pEvent->where, part) )
  4135.                         DoZoomWindow(pWindow, part);
  4136.                     break;
  4137.                     
  4138.                 case inDrag:
  4139.                     {
  4140.                     WindowDataPtr    pData = GetWindowInfo(pWindow);
  4141.                     
  4142.                     if ( (pData) && (pData->dragWindowAligned) )
  4143.                         DragAlignedWindow((WindowPtr) pWindow, pEvent->where, &qd.screenBits.bounds, nil, nil);
  4144.                     else
  4145.                         DragWindow(pWindow, pEvent->where, &qd.screenBits.bounds);
  4146.                     }
  4147.                     break;
  4148.                     
  4149.                 case inMenuBar:                /* process a mouse menu command (if any) */
  4150.                     {
  4151.                     long    menuResult;
  4152.                     
  4153.                     // force these threads to run to completion so the
  4154.                     // contents of the menus are fully initialized
  4155.                     
  4156.                     if (gFontThread != kNoThreadID)
  4157.                         {
  4158.                         gDontYield = true;
  4159.                         SetThreadState(gFontThread, kReadyThreadState, gFontThread);
  4160.                         YieldToThread(gFontThread);
  4161.                         gDontYield = false;
  4162.                         }
  4163.                     if (gAGThread != kNoThreadID)
  4164.                         {
  4165.                         gDontYield = true;
  4166.                         SetThreadState(gAGThread, kReadyThreadState, gAGThread);
  4167.                         YieldToThread(gAGThread);
  4168.                         gDontYield = false;
  4169.                         }
  4170.                     
  4171.                     pWindow = FrontWindow();
  4172.                     AdjustMenus(pWindow, true, false);
  4173.                     menuResult = MenuSelect(pEvent->where);
  4174.                     if ( (gMachineInfo.haveTSM) && (TSMMenuSelect(menuResult)) )
  4175.                         HiliteMenu(0);
  4176.                     else
  4177.                         DoMenuCommand(pWindow, menuResult);
  4178.                     }
  4179.                     break;
  4180.                     
  4181.                 case inSysWindow:            /* let the system handle the mouseDown */
  4182.                     SystemClick(pEvent, pWindow);
  4183.                     break;
  4184.                     
  4185.                 } // switch(part)
  4186.             }
  4187.             break;
  4188.             
  4189.         case keyDown:
  4190.         case autoKey:                        /* check for menukey equivalents */
  4191.             DoKeyEvent(pWindow, pEvent, true);
  4192.             break;
  4193.             
  4194.         case updateEvt:
  4195.             pWindow = (WindowRef) pEvent->message;
  4196.             DoUpdateWindow(pWindow);
  4197.             break;
  4198.  
  4199.         } // switch (pEvent->what)
  4200.     
  4201. } // HandleEvent
  4202.  
  4203. // --------------------------------------------------------------------------------------------------------------
  4204. #pragma segment Main
  4205.  
  4206. static OSErr    DoEventLoop(void)
  4207. {
  4208.     OSErr        anErr = noErr;
  4209.     Boolean        gotEvent;
  4210.     Boolean        trueGotEvent;
  4211.     WindowRef    pWindow;
  4212.     
  4213.     do     {
  4214.         pWindow = LMGetFirstWindow();        // walk all of our windows, even invisible ones
  4215.         
  4216.         DoAdjustCursor(pWindow);
  4217.         gotEvent = WaitNextEvent(everyEvent, &gEvent, DetermineWaitTime(pWindow), gCursorRgn);
  4218.         trueGotEvent = gotEvent;
  4219.  
  4220.         // WNE may close the window if it's owned by some silly extension.
  4221.         pWindow = LMGetFirstWindow();        
  4222.         
  4223.         // let text services handle the event first if it wishes to do so
  4224.         if ( gMachineInfo.haveTSM )
  4225.             {
  4226.             ScriptCode    keyboardScript;
  4227.             WindowRef    theFront = FrontWindow();
  4228.             
  4229.             if (theFront)
  4230.                 {
  4231.                 SetPort((GrafPtr) GetWindowPort(theFront));
  4232.                 
  4233.                 keyboardScript = GetScriptManagerVariable(smKeyScript);
  4234.                 if (FontToScript(qd.thePort->txFont) != keyboardScript)
  4235.                     TextFont(GetScriptVariable(keyboardScript, smScriptAppFond));
  4236.                 }
  4237.             
  4238.             if (TSMEvent(&gEvent))
  4239.                 gotEvent = false;
  4240.             }
  4241.             
  4242.         // let all windows filter this event, and get time if they wish to
  4243.         while (pWindow)
  4244.             {
  4245.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  4246.             Boolean            finishedEvent = false;
  4247.             
  4248.             // help manager for the front window
  4249.             if ( (pWindow == FrontWindow()) && (pData) && (!gMachineInfo.amInBackground) && (HMGetBalloons()) )
  4250.                 {
  4251.                 Point    theMouse, tipLocation;
  4252.                 short    newBalloon = iNoBalloon;
  4253.                 Rect    tempRect;
  4254.                 
  4255.                 // find out where the mouse is                
  4256.                 SetPort((GrafPtr) GetWindowPort(pWindow));
  4257.                 GetMouse(&theMouse);
  4258.                 
  4259.                 // and only do something if we are within the window itself
  4260.                 if (PtInRect(theMouse, &GetWindowPort(pWindow)->portRect))
  4261.                     {
  4262.                     // is it in the vertical scroll bar?
  4263.                     if (pData->vScroll)
  4264.                         {
  4265.                         tempRect = (**(pData->vScroll)).contrlRect;
  4266.                         if (PtInRect(theMouse, &tempRect))
  4267.                             {
  4268.                             newBalloon = iHelpActiveScroll;
  4269.                             if (GetControlMinimum(pData->vScroll) == GetControlMaximum(pData->vScroll))
  4270.                                 newBalloon = iHelpDimVertScroll;
  4271.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  4272.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  4273.                             }
  4274.                         }
  4275.                         
  4276.                     // is it in the horizontal scroll bar?
  4277.                     if (pData->hScroll)
  4278.                         {
  4279.                         tempRect = (**(pData->hScroll)).contrlRect;
  4280.                         if (PtInRect(theMouse, &tempRect))
  4281.                             {
  4282.                             newBalloon = iHelpActiveScroll;
  4283.                             if (GetControlMinimum(pData->hScroll) == GetControlMaximum(pData->hScroll))
  4284.                                 newBalloon = iHelpDimHorizScroll;
  4285.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  4286.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  4287.                             }
  4288.                         }
  4289.                     
  4290.                     // is it in the grow box?
  4291.                     if (pData->hasGrow)
  4292.                         {
  4293.                         CalculateGrowIcon(pData, &tempRect);
  4294.                         if (PtInRect(theMouse, &tempRect))
  4295.                             {
  4296.                             newBalloon = iHelpGrowBox;
  4297.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  4298.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  4299.                             }
  4300.                         }
  4301.                     
  4302.                     // none of the above, must be the content
  4303.                     if (newBalloon == iNoBalloon)
  4304.                         {
  4305.                         newBalloon = iHelpGenericContent;
  4306.                         tempRect = pData->contentRect;
  4307.                         if (pData->pGetBalloon)
  4308.                             (*(pData->pGetBalloon)) (pWindow, pData, &theMouse, &newBalloon, &tempRect);
  4309.                             
  4310.                         tipLocation.h = tempRect.left + kFromTopTipOffset;
  4311.                         tipLocation.v = tempRect.top + kFromTopTipOffset;
  4312.                         }
  4313.                         
  4314.                     // show our new balloon, or remove the old one
  4315.                     if (newBalloon != iNoBalloon)
  4316.                         {
  4317.                         if ( (gMachineInfo.lastBalloonIndex != newBalloon) || (!HMIsBalloon()) )
  4318.                             {
  4319.                             HMMessageRecord    message;
  4320.                             
  4321.                             if (newBalloon != iDidTheBalloon)
  4322.                                 {
  4323.                                 message.hmmHelpType = khmmString;
  4324.                                 GetIndString(message.u.hmmString, kWindowHelpID, newBalloon);
  4325.                                 LocalToGlobal(&tipLocation);
  4326.                                 (void) HMShowBalloon(&message, tipLocation, nil, nil, 0, kDefaultBalloonVariant, 0);
  4327.                                 }
  4328.                             gMachineInfo.lastBalloonIndex = newBalloon;
  4329.                             }
  4330.                         }
  4331.                     else
  4332.                         HMRemoveBalloon();
  4333.                     }
  4334.                     
  4335.                     
  4336.                 }
  4337.                 
  4338.             // if we hit a window we know about, then do filtering
  4339.             if (pData)
  4340.                 {
  4341.                 if (pData->pFilterEvent)
  4342.                     finishedEvent = (*(pData->pFilterEvent)) (pWindow, pData, &gEvent);
  4343.                 }
  4344.  
  4345.             // if filtering indicates complete handling of event, then stop, and
  4346.             // do no regular processing.
  4347.             if (finishedEvent)
  4348.                 {
  4349.                 gotEvent = false;
  4350.                 pWindow = nil;
  4351.                 }
  4352.             else
  4353.                 pWindow = GetNextWindow(pWindow);
  4354.             }
  4355.             
  4356.         if (gotEvent)
  4357.             HandleEvent(&gEvent);
  4358.             
  4359.         // close request?
  4360.         if (gAllDone)
  4361.             {
  4362.             pWindow = FrontWindow();
  4363.             while ((gAllDone) && (pWindow) )
  4364.                 {
  4365.                 WindowRef    nextWindow = GetNextWindow(pWindow);
  4366.                 OSErr        closeError = DoCloseWindow(pWindow, true);
  4367.                 
  4368.                 // window didn't close?  then don't quit
  4369.                 if (pWindow == FrontWindow())
  4370.                     gAllDone = false;
  4371.                     
  4372.                 // something bad happened, then don't quit
  4373.                 if ( (closeError != noErr) /* && (closeError != eUserCanceled) */ )
  4374.                     gAllDone = false;
  4375.                     
  4376.                 pWindow = nextWindow;
  4377.                 }
  4378.             }
  4379.         
  4380.         // our threads are low-priority, so we only give time to them on idle
  4381.         if (gMachineInfo.haveThreads && !trueGotEvent && !gAllDone)
  4382.             YieldToAnyThread();
  4383.         
  4384.         } while (!gAllDone);
  4385.         
  4386.     return anErr;
  4387.     
  4388. } // DoEventLoop
  4389.  
  4390.  
  4391. // --------------------------------------------------------------------------------------------------------------
  4392. // DRAG MANAGEMENT GLOBAL SUPPORT ROUTINES
  4393. // --------------------------------------------------------------------------------------------------------------
  4394.  
  4395. // Globals for our drag handlers
  4396.  
  4397. Boolean                gCanAccept;                // if we can receive the item(s) being dragged
  4398.  
  4399. // --------------------------------------------------------------------------------------------------------------
  4400. #pragma segment Drag
  4401.  
  4402. static pascal OSErr GlobalTrackingHandler(short message, WindowRef pWindow, void *handlerRefCon, DragReference theDragRef)
  4403. {
  4404.     #pragma unused(handlerRefCon)
  4405.  
  4406.     WindowDataPtr pData = GetWindowInfo(pWindow);
  4407.  
  4408.     // Call the tracking handler associated with this type of window. Only allow messages referencing
  4409.     // a specific window to be passed to the handler.
  4410.  
  4411.     if (pData)
  4412.         {    
  4413.         if (pData->pDragTracking)
  4414.             return ((*(pData->pDragTracking)) (pWindow, pData, theDragRef, message));
  4415.         }
  4416.     
  4417.     return noErr;
  4418.  
  4419. } // GlobalTrackingHandler
  4420.  
  4421. DragTrackingHandlerUPP gGlobalTrackingHandler;
  4422.  
  4423. // --------------------------------------------------------------------------------------------------------------
  4424. #pragma segment Drag
  4425.  
  4426. static pascal OSErr GlobalReceiveHandler(WindowRef pWindow, void *handlerRefCon, DragReference theDragRef)
  4427. {
  4428.     #pragma unused(handlerRefCon)
  4429.  
  4430.     WindowDataPtr pData = GetWindowInfo(pWindow);
  4431.     
  4432.     if (pData)
  4433.         {
  4434.         if (pData->pDragTracking)
  4435.             return ((*(pData->pDragReceive)) (pWindow, pData, theDragRef));
  4436.         }
  4437.  
  4438.     return noErr;
  4439.  
  4440. } // GlobalReceiveHandler
  4441.  
  4442. DragReceiveHandlerUPP gGlobalReceiveHandler;
  4443.  
  4444. // --------------------------------------------------------------------------------------------------------------
  4445. //
  4446. // IsOnlyThisFlavor - Given a DragReference and a FlavorType, we iterate through the drag items to determine if
  4447. //                      all are of flavor theType. If this is so, we return true. If any of the items are not
  4448. //                      theType, we return false, indicating that we should not accept the drag.
  4449. //
  4450. #pragma segment Drag
  4451.  
  4452. Boolean IsOnlyThisFlavor(DragReference theDragRef, FlavorType theType)
  4453. {
  4454.     unsigned short    items, index;
  4455.     FlavorFlags        theFlags;
  4456.     ItemReference    itemID;
  4457.     OSErr            anErr = noErr;
  4458.  
  4459.     CountDragItems(theDragRef, &items);
  4460.     
  4461.     for(index = 1; index <= items; index++)
  4462.         {
  4463.         GetDragItemReferenceNumber(theDragRef, index, &itemID);
  4464.  
  4465.         anErr = GetFlavorFlags(theDragRef, itemID, theType, &theFlags);
  4466.         if(anErr == noErr)
  4467.             continue;    // it's okay, this flavor is cool
  4468.  
  4469.         return false;    // this item has at least one flavor we don't like
  4470.         }
  4471.  
  4472.     return true;        // all flavors in this item were cool
  4473.  
  4474. } // IsOnlyThisFlavor
  4475.  
  4476. // --------------------------------------------------------------------------------------------------------------
  4477. //
  4478. // IsDropInFinderTrash - Returns true if the given dropLocation AEDesc is a descriptor of the Finder's Trash.
  4479. //
  4480. #pragma segment Drag
  4481.  
  4482. Boolean IsDropInFinderTrash(AEDesc *dropLocation)
  4483. {
  4484.     OSErr            result;
  4485.     AEDesc            dropSpec;
  4486.     FSSpec            *theSpec;
  4487.     CInfoPBRec        thePB;
  4488.     short            trashVRefNum;
  4489.     long            trashDirID;
  4490.  
  4491.     //    Coerce the dropLocation descriptor into an FSSpec. If there's no dropLocation or
  4492.     //    it can't be coerced into an FSSpec, then it couldn't have been the Trash.
  4493.  
  4494.     if ((dropLocation->descriptorType != typeNull) &&
  4495.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr)) 
  4496.         {
  4497.         unsigned char flags = HGetState(dropSpec.dataHandle);
  4498.         
  4499.         HLock(dropSpec.dataHandle);
  4500.         theSpec = (FSSpec *) *dropSpec.dataHandle;
  4501.  
  4502.         //    Get the directory ID of the given dropLocation object.
  4503.  
  4504.         thePB.dirInfo.ioCompletion = 0L;
  4505.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  4506.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  4507.         thePB.dirInfo.ioFDirIndex = 0;
  4508.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  4509.  
  4510.         result = PBGetCatInfoSync(&thePB);
  4511.  
  4512.         HSetState(dropSpec.dataHandle, flags);
  4513.         AEDisposeDesc(&dropSpec);
  4514.  
  4515.         if (result != noErr)
  4516.             return false;
  4517.  
  4518.         //    If the result is not a directory, it must not be the Trash.
  4519.  
  4520.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  4521.             return false;
  4522.  
  4523.         //    Get information about the Trash folder.
  4524.  
  4525.         FindFolder(theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID);
  4526.  
  4527.         //    If the directory ID of the dropLocation object is the same as the directory ID
  4528.         //    returned by FindFolder, then the drop must have occurred into the Trash.
  4529.  
  4530.         if (thePB.dirInfo.ioDrDirID == trashDirID)
  4531.             return true;
  4532.         }
  4533.  
  4534.     return false;
  4535.  
  4536. } // IsDropInFinderTrash
  4537.  
  4538. // --------------------------------------------------------------------------------------------------------------
  4539. // APPLE EVENT SUPPORT ROUTINES
  4540. // --------------------------------------------------------------------------------------------------------------
  4541. #pragma segment Main
  4542.  
  4543. static OSErr    MissingParameterCheck(
  4544.     AppleEvent     *inputEvent)
  4545. /*
  4546.     This routine checks an input AppleEvent for the missing keyword.
  4547.     If the missing keyword is found, that means that some required
  4548.     parameters were missing (ie, an error). 
  4549.     
  4550.     However, if the missing keyword isn't found, that means that we aren't missing 
  4551.     any required parameters (that is to say, all REQUIRED parameters were supplied
  4552.     by the person who created the event).
  4553.     
  4554.     SOME DAY, THE ABOVE COMMENT WILL MAKE SENSE TO YOU.  IT STILL DOESN'T
  4555.     TO ME AND I WAS THE ONE WHO WROTE IT.
  4556. */
  4557. {
  4558.     OSErr        anErr;
  4559.     AEKeyword    missingKeyword;
  4560.     DescType    ignoredActualType;
  4561.     Size        ignoredActualSize;
  4562.     
  4563.     anErr = AEGetAttributePtr(
  4564.         inputEvent, 
  4565.         keyMissedKeywordAttr,
  4566.         typeWildCard,
  4567.         &ignoredActualType,
  4568.         (Ptr) &missingKeyword,
  4569.         sizeof(AEKeyword),
  4570.         &ignoredActualSize);
  4571.             
  4572.     if (anErr == noErr)
  4573.         anErr = errAEParamMissed;
  4574.     else
  4575.         if (anErr == errAEDescNotFound)
  4576.             anErr = noErr;
  4577.         
  4578.     return anErr;
  4579.     
  4580. } // MissingParameterCheck
  4581.  
  4582. // --------------------------------------------------------------------------------------------------------------
  4583. // Globals for our handlers
  4584. Boolean gQuitAfterPrint = true;
  4585.  
  4586. // --------------------------------------------------------------------------------------------------------------
  4587. #pragma segment Main
  4588.  
  4589. static pascal OSErr    DoOpenApp(
  4590.     AppleEvent     *inputEvent,
  4591.     AppleEvent     *outputEvent,
  4592.     long        handlerRefCon)
  4593. {
  4594. #pragma unused (outputEvent, handlerRefCon)
  4595.  
  4596.     DoCommand(nil, cNew, 0);
  4597.     gQuitAfterPrint = false;
  4598.     
  4599.     // so that the initial document opens more quickly, we don't start
  4600.     // the threads until we get an OpenApp or OpenDocument AppleEvent
  4601.     if (gStarterThread != kNoThreadID)
  4602.         SetThreadState(gStarterThread, kReadyThreadState, gStarterThread);
  4603.     
  4604.     return(MissingParameterCheck(inputEvent));
  4605.     
  4606. } // DoAppOpen
  4607.  
  4608. // --------------------------------------------------------------------------------------------------------------
  4609. #pragma segment Main
  4610.  
  4611. static pascal OSErr    DoQuitApp(
  4612.     AppleEvent     *inputEvent,
  4613.     AppleEvent     *outputEvent,
  4614.     long        handlerRefCon)
  4615. {
  4616. #pragma unused (outputEvent, handlerRefCon)
  4617.  
  4618.     DoCommand(nil, cQuit, 0);
  4619.  
  4620.     return(MissingParameterCheck(inputEvent));
  4621.     
  4622. } // DoQuitApp
  4623.  
  4624. // --------------------------------------------------------------------------------------------------------------
  4625. #pragma segment Main
  4626.  
  4627. static pascal OSErr    DoOpenOrPrint(
  4628.     AppleEvent     *inputEvent,
  4629.     StringPtr    pPrinterName)    // nil == 0, zero length == print to default, other == printer name
  4630. {
  4631.  
  4632.     OSErr        anErr, anErr2;
  4633.     AEDescList    docList;                // list of docs passed in
  4634.     long        index, itemsInList;
  4635.     void*        hPrint;
  4636.     Boolean        wasAlreadyOpen;
  4637.     
  4638.     anErr = AEGetParamDesc( inputEvent, keyDirectObject, typeAEList, &docList);
  4639.     nrequire(anErr, GetFileList);
  4640.  
  4641.     anErr = AECountItems( &docList, &itemsInList);            // how many files passed in
  4642.     nrequire(anErr, CountDocs);
  4643.     for (index = 1; index <= itemsInList; index++)            // handle each file passed in
  4644.         {    
  4645.         AEKeyword    keywd;
  4646.         DescType    returnedType;
  4647.         Size        actualSize;
  4648.         FSSpec        theFSS;    
  4649.  
  4650.         anErr = AEGetNthPtr( &docList, index, typeFSS, &keywd, &returnedType,    // get file's info
  4651.                             (Ptr)(&theFSS), sizeof(theFSS), &actualSize);
  4652.         nrequire(anErr, AEGetNthPtr);
  4653.         
  4654.         {
  4655.         FInfo    theFileInfo;
  4656.         
  4657.         anErr = FSpGetFInfo(&theFSS, &theFileInfo);
  4658.         if (anErr == noErr)
  4659.             anErr = DetermineWindowTypeOrOpen(&theFSS, theFileInfo.fdType, nil, nil, &wasAlreadyOpen);
  4660.             
  4661.         if (anErr == eDocumentWrongKind)
  4662.             {
  4663.             if (pPrinterName)
  4664.                 ConductErrorDialog(anErr, cPrint, cancel);
  4665.             else
  4666.                 ConductErrorDialog(anErr, cOpen, cancel);
  4667.  
  4668.             anErr = noErr;
  4669.             break;
  4670.             }
  4671.             
  4672.         nrequire(anErr, DetermineWindowTypeOrOpen);
  4673.         }
  4674.         
  4675.         if (pPrinterName)
  4676.             {
  4677.             WindowRef        pWindow = FrontWindow();
  4678.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  4679.             
  4680.             if (pData->pPrintPage)
  4681.                 {
  4682.                 if (index == 1)
  4683.                     {
  4684.                     anErr = DoPrintSetup(pWindow, pPrinterName);
  4685.                     if (anErr == noErr)
  4686.                         hPrint = pData->hPrint;
  4687.                     }
  4688.                     
  4689.                 if (anErr == noErr)
  4690.                     anErr = DoPrint(pWindow, hPrint, false);
  4691.                     
  4692.                 if (index != itemsInList)
  4693.                     pData->hPrint = nil;
  4694.                 }
  4695.             
  4696.             if (!wasAlreadyOpen)
  4697.                 DoCloseWindow(pWindow, false);
  4698.  
  4699.             if (anErr != noErr)
  4700.                 break;
  4701.             }
  4702.         }
  4703.  
  4704.     // finally, make sure we didn't miss any parameters
  4705.     anErr2 = MissingParameterCheck(inputEvent);
  4706.     if (anErr == noErr)
  4707.         anErr = anErr2;
  4708.         
  4709. // FALL THROUGH EXCEPTION HANDLING
  4710. DetermineWindowTypeOrOpen:
  4711. AEGetNthPtr:
  4712. CountDocs:
  4713.     // done with doc list
  4714.     (void) AEDisposeDesc( &docList);                        
  4715.     
  4716. GetFileList:
  4717.  
  4718.     // don't report cancels from prints
  4719.     if (pPrinterName)
  4720.         {
  4721.         if ( (anErr == iPrAbort) || (anErr == gxPrUserAbortErr) )
  4722.             anErr = noErr;
  4723.         }
  4724.     
  4725.     if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) && (anErr != eUserCanceled) )
  4726.         {
  4727.         if (pPrinterName)
  4728.             ConductErrorDialog(anErr, cPrint, cancel);
  4729.         else
  4730.             ConductErrorDialog(anErr, cOpen, cancel);
  4731.         }
  4732.         
  4733.     return anErr;
  4734.     
  4735. } // DoOpenOrPrint
  4736.  
  4737. // --------------------------------------------------------------------------------------------------------------
  4738. #pragma segment Main
  4739.  
  4740. static pascal OSErr    DoOpenDocument(
  4741.     AppleEvent     *inputEvent,
  4742.     AppleEvent     *outputEvent,
  4743.     long        handlerRefCon)
  4744. {
  4745. #pragma unused (outputEvent, handlerRefCon)
  4746.  
  4747.     OSErr        anErr;
  4748.     
  4749.     if (IsCommandEnabled(cOpen))
  4750.         {
  4751.         gQuitAfterPrint = false;
  4752.         anErr = DoOpenOrPrint(inputEvent, nil);
  4753.         }
  4754.     else
  4755.         {
  4756.         anErr = errAEEventNotHandled;
  4757.         ConductErrorDialog(anErr, cOpen, cancel);
  4758.         }
  4759.         
  4760.     // so that the initial document opens more quickly, we don't start
  4761.     // the threads until we get an OpenApp or OpenDocument AppleEvent
  4762.     if (gStarterThread != kNoThreadID)
  4763.         SetThreadState(gStarterThread, kReadyThreadState, gStarterThread);
  4764.     
  4765.     return anErr;
  4766.     
  4767. } // DoOpenDocument
  4768.  
  4769. // --------------------------------------------------------------------------------------------------------------
  4770. #pragma segment Main
  4771.  
  4772. static pascal OSErr    DoPrintDocument(
  4773.     AppleEvent     *inputEvent,
  4774.     AppleEvent     *outputEvent,
  4775.     long        handlerRefCon)
  4776. {
  4777. #pragma unused (outputEvent, handlerRefCon)
  4778.     OSErr        anErr;
  4779.     FSSpec        printerFSS;
  4780.     AEDescList    dtpList;                // list of docs passed in
  4781.     
  4782.     if (IsCommandEnabled(cOpen))
  4783.         {
  4784.         // try to find out if this doc was dropped onto a printer
  4785.         anErr = AEGetAttributeDesc( inputEvent, keyOptionalKeywordAttr, typeAEList, &dtpList);
  4786.     
  4787.         if (anErr == noErr)                                            // doc dragged to dtp?
  4788.             {
  4789.             AEKeyword    keywd;
  4790.             DescType    returnedType;
  4791.             Size        actualSize;
  4792.     
  4793.             anErr = AEGetNthPtr( &dtpList, 1, typeFSS, &keywd, &returnedType,    // get dtp info
  4794.                             (Ptr)(&printerFSS), sizeof(printerFSS), &actualSize);
  4795.             }
  4796.             
  4797.         // if it wasn't, that's not an error, just print normally
  4798.         if (anErr != noErr)
  4799.             {
  4800.             printerFSS.name[0] = 0;
  4801.             anErr = noErr;
  4802.             }
  4803.             
  4804.         anErr = DoOpenOrPrint(inputEvent, &printerFSS.name[0]);
  4805.         
  4806.         // if we are opened just for printing -- quit afterwards
  4807.         if (gQuitAfterPrint)
  4808.             DoCommand(nil, cQuit, 0);
  4809.         }
  4810.     else
  4811.         {
  4812.         anErr = errAEEventNotHandled;
  4813.         ConductErrorDialog(anErr, cPrint, cancel);
  4814.         }
  4815.         
  4816.     return anErr;
  4817.     
  4818. } // DoPrintDocument
  4819.  
  4820. #if GENERATINGCFM
  4821.     static RoutineDescriptor    gDoOpenAppRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoOpenApp);
  4822.     static AEEventHandlerUPP    gDoOpenApp = &gDoOpenAppRD;
  4823.  
  4824.     static RoutineDescriptor    gDoQuitAppRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoQuitApp);
  4825.     static AEEventHandlerUPP    gDoQuitApp = &gDoQuitAppRD;
  4826.  
  4827.     static RoutineDescriptor    gDoOpenDocumentRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoOpenDocument);
  4828.     static AEEventHandlerUPP    gDoOpenDocument = &gDoOpenDocumentRD;
  4829.  
  4830.     static RoutineDescriptor    gDoPrintDocumentRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoPrintDocument);
  4831.     static AEEventHandlerUPP    gDoPrintDocument = &gDoPrintDocumentRD;
  4832. #else
  4833.     static AEEventHandlerUPP    gDoOpenApp = (AEEventHandlerUPP) DoOpenApp;
  4834.     static AEEventHandlerUPP    gDoQuitApp = (AEEventHandlerUPP) DoQuitApp;
  4835.     static AEEventHandlerUPP    gDoOpenDocument = (AEEventHandlerUPP) DoOpenDocument;
  4836.     static AEEventHandlerUPP    gDoPrintDocument = (AEEventHandlerUPP) DoPrintDocument;
  4837. #endif
  4838. // --------------------------------------------------------------------------------------------------------------
  4839. #pragma segment Main
  4840.  
  4841. static pascal OSErr SimpleTextCoachHandler(Rect *pRect, Ptr name, long refCon)
  4842. {
  4843. #pragma unused (refCon)
  4844.  
  4845.     OSErr            anErr = noErr;
  4846.     WindowRef        pWindow = FrontWindow();
  4847.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  4848.     
  4849.     if ((pData) && (pData->pGetCoachRectangle))
  4850.         anErr = (*(pData->pGetCoachRectangle)) (pWindow, pData, pRect, name);
  4851.         
  4852.     return(anErr);
  4853.     
  4854. } // SimpleTextCoachHandler
  4855.  
  4856. // --------------------------------------------------------------------------------------------------------------
  4857. // MAIN INITIALIZE/SHUTDOWN/LOOP ROUTINES
  4858. // --------------------------------------------------------------------------------------------------------------
  4859. #pragma segment Main
  4860.  
  4861. static pascal void* StarterThread(void* threadParam)
  4862. {
  4863.     #pragma unused(threadParam)
  4864.     
  4865.     /*
  4866.         All threads, including the starter thread, are initially created
  4867.         in a suspended state. The starter thread is made ready to run when
  4868.         we get an open application or open document event. It runs until
  4869.         there are no activate or update events pending, and then starts the
  4870.         font menu and Apple Guide threads. This gives much better performance
  4871.         for the initial creation and update of a document, because the threads
  4872.         (especially the font thread) chew up a lot of time at first.
  4873.         
  4874.         The starter thread isn't really necessary - we could get the same
  4875.         effect by just checking in the event loop for activate/update events -
  4876.         but hey, it's a lot easier to do it this way, doesn't cost much, and
  4877.         isn't that what threads are for?
  4878.     */
  4879.     
  4880.     for (;;)
  4881.         {
  4882.         EventRecord        er;
  4883.     
  4884.         YieldToAnyThread();
  4885.         
  4886.         if (!EventAvail(activMask | updateMask, &er))
  4887.             {
  4888.             if (gFontThread != kNoThreadID)
  4889.                 SetThreadState(gFontThread, kReadyThreadState, gFontThread);
  4890.             if (gAGThread != kNoThreadID)
  4891.                 SetThreadState(gAGThread, kReadyThreadState, gAGThread);
  4892.             
  4893.             break;
  4894.             }
  4895.         }
  4896.     
  4897.     gStarterThread = kNoThreadID;
  4898.     return NULL;
  4899.     
  4900. } // StarterThread
  4901.  
  4902. // --------------------------------------------------------------------------------------------------------------
  4903. #pragma segment Initialize
  4904.  
  4905. static OSErr CreateThread(ThreadEntryProcPtr pThread, void* threadParam, ThreadID* ptid)
  4906. {
  4907.     OSErr    anErr;
  4908.     
  4909.     anErr = NewThread(kCooperativeThread, pThread, threadParam, 0, kNewSuspend,
  4910.         &gThreadResults, ptid);
  4911.         
  4912.     if (anErr == noErr && gStarterThread == kNoThreadID)
  4913.         {
  4914.         anErr = NewThread(kCooperativeThread, StarterThread, NULL, 0, kNewSuspend,
  4915.             &gThreadResults, &gStarterThread);
  4916.         if (anErr != noErr)
  4917.             DisposeThread(*ptid, &gThreadResults, false);
  4918.             // anErr remains != noErr
  4919.         }
  4920.         
  4921.     return anErr;
  4922. }
  4923.         
  4924. // --------------------------------------------------------------------------------------------------------------
  4925. // must be in Main because it runs in a thread and we don't want the segment unloaded
  4926. // while some other thread is running
  4927. #pragma segment Main
  4928.  
  4929. static OSErr FindSimpleTextGuideFile(FSSpec *pSpec)
  4930. {
  4931.     OSErr    anErr = fnfErr;
  4932.     short    numDBs, index;
  4933.     short    folderIndex;
  4934.     
  4935.     for (folderIndex = 0; folderIndex < 3; ++folderIndex)
  4936.         {
  4937.         switch (folderIndex)
  4938.             {
  4939.             case 0:
  4940.                 FindFolder(-1, kPreferencesFolderType, false, &pSpec->vRefNum, &pSpec->parID);
  4941.                 break;
  4942.  
  4943.             case 1:
  4944.                 FindFolder(-1, kExtensionFolderType, false, &pSpec->vRefNum, &pSpec->parID);
  4945.                 break;
  4946.  
  4947.             case 2:
  4948.                 FindFolder(-1, kSystemFolderType, false, &pSpec->vRefNum, &pSpec->parID);
  4949.                 break;
  4950.             }
  4951.         
  4952.         numDBs = AGFileGetDBCount(pSpec->vRefNum, pSpec->parID, kAGFileDBTypeAny, false);
  4953.         
  4954.         if (!gDontYield)
  4955.             YieldToAnyThread();
  4956.         
  4957.         for (index = 0; index < numDBs; ++index)
  4958.             {
  4959.             if (!gDontYield)
  4960.                 YieldToAnyThread();
  4961.             
  4962.             if (AGFileGetIndDB(pSpec->vRefNum, pSpec->parID, kAGFileDBTypeAny, false, index+1, pSpec) == noErr)
  4963.                 {
  4964.                 OSType    creator;
  4965.                 
  4966.                 if ((AGFileGetHelpMenuAppCreator(pSpec, &creator) == noErr) && (creator == 'ttxt'))
  4967.                     return(noErr);
  4968.                 }
  4969.             }
  4970.         }
  4971.     
  4972.     return(anErr);
  4973.     
  4974. } // FindSimpleTextGuideFile
  4975.  
  4976. // --------------------------------------------------------------------------------------------------------------
  4977. // must be in Main because it runs in a thread and we don't want the segment unloaded
  4978. // while some other thread is running
  4979. #pragma segment Main
  4980.  
  4981. static pascal void* AGThread(void *threadParam)
  4982. {
  4983.     #pragma unused(threadParam)
  4984.     
  4985.     if ( !(AGGetAvailableDBTypes() & kAGDBTypeBitAny) )
  4986.         {
  4987.         // if we find one, add it to the help menu and add the right command
  4988.         if (FindSimpleTextGuideFile(&gAGSpec) == noErr)
  4989.             {
  4990.             MenuHandle    helpMenu;
  4991.                         
  4992.             if (HMGetHelpMenuHandle(&helpMenu) == noErr)
  4993.                 {
  4994.                 Str255    tempString;
  4995.                 short    rnSave;
  4996.                 
  4997.                 AGFileGetDBMenuName(&gAGSpec, tempString);
  4998.                 AppendMenu(helpMenu, tempString);
  4999.                 
  5000.                 // since we're in a separate thread, so other resource file may
  5001.                 // be open in front of SimpleText's resource fork
  5002.                 rnSave = CurResFile();
  5003.                 UseResFile(gApplicationResFile);
  5004.                 
  5005.                 GetIndString(tempString, kMiscStrings, iHelpMenuCommand);
  5006.                 
  5007.                 UseResFile(rnSave);
  5008.                 
  5009.                 if (tempString[0] != 0)
  5010.                     SetItemCmd(helpMenu, CountMItems(helpMenu), tempString[1]);
  5011.                 }
  5012.             }
  5013.         }
  5014.     
  5015.     // if we have a database, then install a coach handler
  5016.     if ( (AGGetAvailableDBTypes() & kAGDBTypeBitAny) )
  5017.         {
  5018.         AGInstallCoachHandler(NewCoachReplyProc(SimpleTextCoachHandler), 0, &gAGCoachRefNum);
  5019.         }
  5020.     
  5021.     gAGThread = kNoThreadID;
  5022.     return NULL;
  5023.     
  5024. } // AGThread
  5025.  
  5026. // --------------------------------------------------------------------------------------------------------------
  5027. #pragma segment Initialize
  5028.  
  5029. static void FindAndInstallSimpleTextGuide(void)
  5030. {
  5031.     OSErr    anErr = fnfErr;
  5032.     
  5033.     if (gMachineInfo.haveThreads)
  5034.         anErr = CreateThread(AGThread, NULL, &gAGThread);
  5035.     
  5036.     if (anErr != noErr)
  5037.         AGThread(NULL);
  5038.         
  5039. } // FindAndinstallSimpleTextGuide
  5040.  
  5041. // --------------------------------------------------------------------------------------------------------------
  5042. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5043. // while some other thread is running
  5044. #pragma segment Main
  5045.  
  5046. static long SortAndAddMenu(MenuHandle menu, Str255 newItem)
  5047. {
  5048.     short    numInMenu = CountMItems(menu);
  5049.     short    i;
  5050.     Str255    oldItem;
  5051.     
  5052.     for (i = 1; i <= numInMenu; ++i)
  5053.         {
  5054.         GetMenuItemText(menu, i, oldItem);
  5055.         switch(IUCompString(newItem, oldItem))
  5056.             {
  5057.             // already in?  Return index
  5058.             case 0:
  5059.                 return(i);
  5060.                 break;
  5061.                 
  5062.             // less than, keep scanning
  5063.             case 1:
  5064.                 break;
  5065.                 
  5066.             // greater than, add back one
  5067.             case -1:
  5068.                 InsertMenuItem(menu, "\pTom Dowdy", i-1);
  5069.                 SetMenuItemText(menu, i, newItem);
  5070.                 return(i);
  5071.                 break;
  5072.             }
  5073.         }
  5074.         
  5075.     // fall off the end?  add at the end
  5076.     InsertMenuItem(menu, "\pTom Dowdy", numInMenu);
  5077.     SetMenuItemText(menu, numInMenu+1, newItem);
  5078.             
  5079.     return(numInMenu+1);
  5080.     
  5081. } // SortAndAddMenu
  5082.  
  5083. // --------------------------------------------------------------------------------------------------------------
  5084. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5085. // while some other thread is running
  5086. #pragma segment Main
  5087.  
  5088. static void AddToGlobalList(gxFont fontID, short fond, Style styleBits)
  5089. {
  5090.     if (!gFontMappingList)
  5091.         gFontMappingList = (FontMappingHandle)NewHandle(0);
  5092.         
  5093.     if (gFontMappingList)
  5094.         {
  5095.         Size            oldSize = GetHandleSize((Handle) gFontMappingList);
  5096.         FontMappingPtr    pList;
  5097.         
  5098.         SetHandleSize((Handle)gFontMappingList, oldSize + sizeof(FontMappingRecord));
  5099.         pList = &(*gFontMappingList)[oldSize/sizeof(FontMappingRecord)];
  5100.         pList->fontID = fontID;
  5101.         pList->qdFont = fond;
  5102.         pList->qdStyle = styleBits;
  5103.         }
  5104.         
  5105. } // AddToGlobalList
  5106.  
  5107. // --------------------------------------------------------------------------------------------------------------
  5108. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5109. // while some other thread is running
  5110. #pragma segment Main
  5111.  
  5112. static void AddEachEntry(Handle fond, short* sp, short entries, gxStyle theStyle, MenuHandle menu, short* pStylesUsed)
  5113. {
  5114.     gxFont            fontID;
  5115.     long            length;
  5116.     short             resID;
  5117.     OSType             resType;
  5118.     Str255             resName;
  5119.     Style            styleBits;
  5120.     
  5121.     GetResInfo(fond, &resID, &resType, resName);
  5122.     
  5123.     for (; entries >= 0; --entries)
  5124.         {   
  5125.         if (*sp == 0)
  5126.             {   
  5127.             styleBits = sp[1];
  5128.  
  5129.             // map the font ID and the style bits for this style into GX space
  5130.             (void)GXConvertQDFont(theStyle, resID, styleBits);
  5131.             
  5132.             if (!gDontYield)
  5133.                 YieldToAnyThread();
  5134.     
  5135.             fontID = GXGetStyleFont(theStyle);
  5136.             if (fontID)
  5137.                 length = GXFindFontName(fontID, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, nil, nil);
  5138.             else
  5139.                 length = 0;
  5140.                 
  5141.             if (!gDontYield)
  5142.                 YieldToAnyThread();
  5143.     
  5144.             if (length)
  5145.                 {
  5146.                 unsigned char     * name;
  5147.                 short            where;
  5148.                 
  5149.                 name = (unsigned char*) NewPtr(length+1);
  5150.                 if (name)
  5151.                     {
  5152.                     MenuHandle    subMenu;
  5153.                     short        mark;
  5154.  
  5155.                     // add this font to the list we know about
  5156.                     AddToGlobalList(fontID, resID, styleBits);
  5157.  
  5158.                     // find the family name for this font
  5159.                     GXFindFontName(fontID, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, &name[1], nil);
  5160.                     name[0] = length;
  5161.                     
  5162.                     // add the name if new. Then add sub menus
  5163.                     where = SortAndAddMenu(menu, name);
  5164.                     
  5165.                     // do we already have a sub menu?  Or does this font have multiple styles?
  5166.                     GetItemMark(menu, where, &mark);
  5167.                     if ( (mark != 0) || (GXFindFonts(fontID, 0, 0, 0, 0, 0, nil, 1, gxSelectToEnd, nil) > 1) )
  5168.                         {
  5169.                         if (!gDontYield)
  5170.                             YieldToAnyThread();
  5171.     
  5172.                         // make a new menu or grab the old one
  5173.                         if (mark == 0)
  5174.                             subMenu = NewMenu(mFontSubMenusStart + *pStylesUsed, name);
  5175.                         else
  5176.                             subMenu = GetMenuHandle(mark);
  5177.                         DisposePtr((Ptr) name);
  5178.                         
  5179.                         length = GXFindFontName(fontID, gxStyleFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, nil, nil);
  5180.                         if (length)
  5181.                             {
  5182.                             name = (unsigned char*) NewPtr(length+1);
  5183.                             if (name)
  5184.                                 {
  5185.                                 GXFindFontName(fontID, gxStyleFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, &name[1], nil);
  5186.                                 name[0] = length;
  5187.                                 (void) SortAndAddMenu(subMenu, name);
  5188.                                 DisposePtr((Ptr) name);
  5189.                                 }
  5190.                             }
  5191.                         
  5192.                         // if new menu, add to the master
  5193.                         if (mark == 0)
  5194.                             {
  5195.                             InsertMenu(subMenu, -1);
  5196.                             SetItemCmd(menu, where, hMenuCmd);
  5197.                             SetItemMark(menu, where, mFontSubMenusStart + *pStylesUsed);
  5198.                             (*pStylesUsed)++;
  5199.                             }
  5200.                         }
  5201.                     else
  5202.                         DisposePtr((Ptr) name);
  5203.  
  5204.                     }
  5205.                 }
  5206.                 
  5207.             } // if (*sp == 0)
  5208.             
  5209.         sp += 3;        /* three elements in the FAT */
  5210.         
  5211.         if (!gDontYield)
  5212.             YieldToAnyThread();
  5213.  
  5214.         } // for (# entries)
  5215. } // AddEachEntry
  5216.  
  5217. // --------------------------------------------------------------------------------------------------------------
  5218. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5219. // while some other thread is running
  5220. #pragma segment Main
  5221.  
  5222. static pascal void* FontsThread(void *threadParam)
  5223. {
  5224.     MenuHandle    menu = (MenuHandle) threadParam;
  5225.     long        numberFonts;
  5226.     long        i;
  5227.     short        stylesUsed = 0;
  5228.     gxStyle        theStyle;
  5229.     Boolean        menusAdjusted = false;
  5230.     
  5231.     theStyle = GXNewStyle();
  5232.     
  5233.     numberFonts = CountResources('FOND');
  5234.     for (i = 1; i <= numberFonts; ++i)
  5235.         {
  5236.         Handle     fond = GetIndResource('FOND', i);
  5237.         
  5238.         if (!ResError() && fond && *fond)
  5239.             do
  5240.             {   
  5241.             short* sp = (short*)(*fond + sizeof(FamRec));
  5242.             short entries = *sp++;
  5243.             
  5244.             AddEachEntry(fond, sp, entries, theStyle, menu, &stylesUsed);
  5245.             
  5246.             // now that there are some fonts in the font menu, make sure the menu's enabled
  5247.             if (!menusAdjusted && CountMItems(menu) > 0)
  5248.                 {
  5249.                 AdjustMenus(FrontWindow(), true, false);
  5250.                 menusAdjusted = true;
  5251.                 }
  5252.                 
  5253.             } while ((fond = GetNextFOND(fond)) != 0);
  5254.             
  5255.         } // for (# fonts)
  5256.  
  5257.     GXDisposeStyle(theStyle);
  5258.     
  5259.     gFontThread = kNoThreadID;
  5260.     return 0;
  5261. } // FontsThread
  5262.  
  5263. // --------------------------------------------------------------------------------------------------------------
  5264. #pragma segment Initialize
  5265.  
  5266. static OSErr BuildFontMenu(MenuHandle menu)
  5267. {
  5268.     OSErr    anErr = noErr;
  5269.     
  5270.     if (gMachineInfo.haveGX)
  5271.         {
  5272.         (void) DoStartupGX();
  5273.         
  5274.         // prime the font cache so we don't spend time doing this in the font thread
  5275.         GXGetDefaultFont();
  5276.         
  5277.         anErr = paramErr;
  5278.         if (gMachineInfo.haveThreads)
  5279.             anErr = CreateThread(FontsThread, menu, &gFontThread);
  5280.  
  5281.         if (anErr != noErr)
  5282.             {
  5283.             FontsThread(menu);
  5284.             anErr = noErr;
  5285.             }
  5286.         }
  5287.     else
  5288.         AppendResMenu(menu, 'FONT');
  5289.     
  5290.     return(anErr);
  5291.     
  5292. } // BuildFontMenu
  5293.  
  5294. // --------------------------------------------------------------------------------------------------------------
  5295. #pragma segment Initialize
  5296.  
  5297. static OSErr    DoInitialize(void)
  5298. {
  5299.     short                count;            // loop counter
  5300.     Handle                menuBar;        // for loading our menus in
  5301.     gxGraphicsError        anErr = noErr;    // any errors we get, none so far
  5302.     long                version;        // version for Gestalt calls
  5303.     
  5304.     InitGraf((Ptr) &qd.thePort);
  5305.     InitFonts();
  5306.     InitWindows();
  5307.     InitMenus();
  5308.     TEInit();
  5309.     InitDialogs(nil);
  5310.     InitCursor();
  5311.     
  5312.     gAllDone = false;
  5313.     
  5314.     // check that the system is correct to handle things
  5315.     SysEnvirons(1, &gMachineInfo.theEnvirons);
  5316.     if (gMachineInfo.theEnvirons.systemVersion < 0x0700)
  5317.         {
  5318.         // Wait for app to come to front
  5319.         for (count = 1; count <= 3; ++count)
  5320.             EventAvail(everyEvent, &gEvent);
  5321.             
  5322.         anErr = eMachineToOld;
  5323.         nrequire(anErr, SysEnvirons);
  5324.         }
  5325.  
  5326.     gMachineInfo.lastBalloonIndex = iNoBalloon;
  5327.     gMachineInfo.amInBackground = false;
  5328.     gMachineInfo.documentCount  = 1;
  5329.     gMachineInfo.haveQuickTime     = (Gestalt(gestaltQuickTime, &version) == noErr);
  5330.     gMachineInfo.haveRecording     = (Gestalt(gestaltSoundAttr, &version) == noErr) && ((version & (1<<gestaltHasSoundInputDevice)) != 0);
  5331.     gMachineInfo.haveTTS         = (Gestalt(gestaltSpeechAttr, &version) == noErr) && ((version & (1<<gestaltSpeechMgrPresent)) != 0);
  5332.     gMachineInfo.haveGX            = (Gestalt(gestaltGXVersion, &version) == noErr);
  5333.     gMachineInfo.haveTSM         = (Gestalt(gestaltTSMgrVersion, &version) == noErr) && (version >= 1);
  5334.     gMachineInfo.haveTSMTE         = (Gestalt(gestaltTSMTEAttr, &version) == noErr) && ((version & (1<<gestaltTSMTE)) != 0);
  5335.     gMachineInfo.haveDragMgr    = (Gestalt(gestaltDragMgrAttr, &version) == noErr) && ((version & (1<<gestaltDragMgrPresent)) != 0) &&
  5336.                                     (Gestalt(gestaltTEAttr, &version) == noErr) && ((version & (1<<gestaltTEHasGetHiliteRgn)) != 0);
  5337.     gMachineInfo.haveThreeD        = false;
  5338.     gMachineInfo.haveAppleGuide    = (Gestalt(gestaltHelpMgrAttr, &version) == noErr) && ((version & (1<<gestaltAppleGuidePresent)) != 0);
  5339.     gMachineInfo.haveThreads    = (Gestalt(gestaltThreadMgrAttr, &version) == noErr) && ((version & (1<<gestaltThreadMgrPresent)) != 0);
  5340.     
  5341.     gMachineInfo.haveNavigationServices  = NavServicesAvailable();
  5342.     
  5343.     #if GENERATINGPOWERPC
  5344.         {
  5345.         CFragConnectionID    connID;
  5346.         Ptr                 mainAddr;
  5347.         Str255                errName;
  5348.         
  5349.         if ( (gMachineInfo.haveQuickTime)     && (GetSharedLibrary("\pQuickTimeLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5350.             gMachineInfo.haveQuickTime = false;
  5351.         if ( (gMachineInfo.haveTTS)         && (GetSharedLibrary("\pSpeechLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5352.             gMachineInfo.haveTTS = false;
  5353.         if ( (gMachineInfo.haveGX)             && (GetSharedLibrary("\pQuickDrawGXLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5354.             gMachineInfo.haveGX = false;
  5355.         if ( (gMachineInfo.haveDragMgr)        && (GetSharedLibrary("\pDragLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5356.             gMachineInfo.haveDragMgr = false;
  5357.         if ( (gMachineInfo.haveThreads)     && (GetSharedLibrary("\pThreadsLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5358.             gMachineInfo.haveThreads = false;
  5359.         }
  5360.     #endif
  5361.     
  5362.  
  5363.     // initialize text services if they exist
  5364.     if (gMachineInfo.haveTSMTE)
  5365.         {
  5366.         if (InitTSMAwareApplication() != noErr)
  5367.             {
  5368.             gMachineInfo.haveTSM = false;
  5369.             gMachineInfo.haveTSMTE = false;
  5370.             }
  5371.         }
  5372.         
  5373.     // save away info we need from the get-go    
  5374.     gApplicationResFile = CurResFile();
  5375.     gCursorRgn = NewRgn();
  5376.  
  5377.     // load up the menus
  5378.     menuBar = (Handle) GetNewMBar(rMenuBar);            /* read menus into menu bar */
  5379.     anErr = ResError();
  5380.     if ( (anErr == noErr) && (menuBar == nil) )
  5381.         anErr = resNotFound;
  5382.     nrequire(anErr, GetNewMBar);
  5383.     
  5384.     // install menus
  5385.     SetMenuBar(menuBar);    
  5386.     DisposeHandle(menuBar);
  5387.  
  5388.     // build the Apple menu
  5389.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');    /* add DA names to Apple menu */
  5390.     
  5391.     // haven't yet done a startup of QuickDraw GX
  5392.     if (gMachineInfo.haveGX)
  5393.         gMachineInfo.haveStartedGX = false;
  5394.  
  5395.     // Build the font menu
  5396.     anErr = BuildFontMenu(GetMenuHandle(mFont));
  5397.     nrequire(anErr, BuildFontMenu);
  5398.     
  5399.     // insert our heirarchical menus
  5400.     {
  5401.     MenuHandle     menu = GetMenu( mVoices );
  5402.     short        menuID, itemID;
  5403.     
  5404.     InsertMenu( menu, hierMenu );
  5405.     
  5406.     CommandToIDs(cSelectVoice, &menuID, &itemID);
  5407.     menu = GetMenuHandle(menuID);
  5408.  
  5409.     SetItemCmd( menu, itemID, hMenuCmd );
  5410.     SetItemMark( menu, itemID, mVoices );
  5411.     }
  5412.  
  5413.     AdjustMenus(nil, true, false);
  5414.     DrawMenuBar();
  5415.     
  5416.     // start up QuickTime, but problems result in us pretending not to have it
  5417.     if (gMachineInfo.haveQuickTime)
  5418.         if (EnterMovies() != noErr)
  5419.             gMachineInfo.haveQuickTime = false;
  5420.         
  5421.     // Install AppleEvent handlers for the base classes
  5422.  
  5423.     #define INSTALL(event, handler) \
  5424.             AEInstallEventHandler(kCoreEventClass, event, handler, 0, false)
  5425.  
  5426.     INSTALL (kAEOpenApplication, gDoOpenApp);
  5427.     INSTALL (kAEQuitApplication, gDoQuitApp);
  5428.     INSTALL (kAEOpenDocuments,   gDoOpenDocument);
  5429.     INSTALL (kAEPrintDocuments,  gDoPrintDocument);
  5430.  
  5431.     #undef INSTALL
  5432.  
  5433.     // Install our global dragging procs, but only if we have Drag and Drop. An error results
  5434.     // in us pretending that we don't have drag support. Notice that in the test above, we also
  5435.     // require TextEdit to have TEGetHiliteRgn avalilable, which is always the case with the
  5436.     // present Drag Manager.
  5437.  
  5438.     if (gMachineInfo.haveDragMgr)
  5439.         {
  5440.         gGlobalTrackingHandler = NewDragTrackingHandlerProc(GlobalTrackingHandler);
  5441.         gGlobalReceiveHandler = NewDragReceiveHandlerProc(GlobalReceiveHandler);
  5442.         
  5443.         anErr = InstallTrackingHandler(gGlobalTrackingHandler, nil, nil);
  5444.  
  5445.         if (anErr == noErr)
  5446.             {
  5447.             anErr = InstallReceiveHandler(gGlobalReceiveHandler, nil, nil);
  5448.  
  5449.             if (anErr != noErr)
  5450.                 {
  5451.                 RemoveTrackingHandler(gGlobalTrackingHandler, nil);
  5452.                 gMachineInfo.haveDragMgr = false;
  5453.                 }
  5454.             }
  5455.         else
  5456.             gMachineInfo.haveDragMgr = false;
  5457.         }
  5458.  
  5459.  
  5460.     // verify that the AppleGuide database is available, and if it isn't, try to find
  5461.     // it from other places -- but don't bother on 2.1, because they made changes
  5462.     // to break our location finding (sigh!)
  5463.     if  (
  5464.         (gMachineInfo.haveAppleGuide) && 
  5465.         ( (Gestalt('ag_v', &version) != noErr) || (version < 0x00000210) )
  5466.         )
  5467.         FindAndInstallSimpleTextGuide();
  5468.         
  5469.  
  5470.     return noErr;
  5471.     
  5472.     
  5473. // EXCEPTION HANDLING
  5474. BuildFontMenu:
  5475. GetNewMBar:
  5476. SysEnvirons:
  5477.     ConductErrorDialog(anErr, cNull, cancel);
  5478.     
  5479.     return anErr;
  5480.  
  5481. } // DoInitialize
  5482.  
  5483. // --------------------------------------------------------------------------------------------------------------
  5484. #pragma segment Terminate
  5485.  
  5486. static OSErr    DoTerminate(void)
  5487. {
  5488.     OSErr    anErr = noErr;
  5489.     
  5490.     if (gFontThread != kNoThreadID)
  5491.         DisposeThread(gFontThread, &gThreadResults, false);
  5492.     if (gAGThread != kNoThreadID)
  5493.         DisposeThread(gAGThread, &gThreadResults, false);
  5494.     if (gStarterThread != kNoThreadID)
  5495.         DisposeThread(gStarterThread, &gThreadResults, false);
  5496.  
  5497.     if (gMachineInfo.haveQuickTime)
  5498.         ExitMovies();
  5499.  
  5500.     if ( (gMachineInfo.haveGX) && (GXGetGraphicsClient() != nil) )
  5501.         {
  5502.         GXExitPrinting();
  5503.         GXExitGraphics();
  5504.         }
  5505.         
  5506.     if (gMachineInfo.haveTSMTE)
  5507.         CloseTSMAwareApplication();
  5508.  
  5509.     if (gMachineInfo.haveDragMgr)
  5510.         {
  5511.         RemoveReceiveHandler(gGlobalReceiveHandler, nil);
  5512.         RemoveTrackingHandler(gGlobalTrackingHandler, nil);
  5513.         }
  5514.         
  5515.     if (gMachineInfo.haveAppleGuide)
  5516.         {
  5517.         if ((gAGRefNum != -1) && AGIsDatabaseOpen(gAGRefNum))
  5518.             {
  5519.             AGClose(&gAGRefNum);
  5520.             gAGRefNum = -1;
  5521.             }
  5522.         if (gAGCoachRefNum != -1)
  5523.             AGRemoveCoachHandler(&gAGCoachRefNum);
  5524.         }
  5525.  
  5526.     return anErr;
  5527.     
  5528. } // DoTerminate
  5529.  
  5530. // --------------------------------------------------------------------------------------------------------------
  5531. #pragma segment Main
  5532.  
  5533. main(void)
  5534. {
  5535.     OSErr    anErr;
  5536.     
  5537. #ifndef __MWERKS__
  5538.     UnloadSeg((Ptr) _DataInit);                        /* note that _DataInit must not be in Main! */
  5539. #endif
  5540.     MaxApplZone();                                    /* expand the heap so code segments load at the top */
  5541.     MoreMasters(); MoreMasters(); MoreMasters();     /* we love handles */
  5542.     anErr = DoInitialize();
  5543.     UnloadSeg((Ptr) DoInitialize);                    
  5544.     if (anErr == noErr)
  5545.         {
  5546.         DoEventLoop();
  5547.  
  5548. // REVIEW: don't want to unload the segment we're in!!
  5549. //        UnloadSeg((Ptr) DoEventLoop);
  5550.         DoTerminate();                    
  5551.         }
  5552.     return 0;
  5553. } // main
  5554.